diff --git a/code/mobile/android/PhoneVR/ALVR b/code/mobile/android/PhoneVR/ALVR index 1d121fa7..938ccba1 160000 --- a/code/mobile/android/PhoneVR/ALVR +++ b/code/mobile/android/PhoneVR/ALVR @@ -1 +1 @@ -Subproject commit 1d121fa7e0761ef26473c6b3f3fbd9b6051ce033 +Subproject commit 938ccba1186e957142a06687b2275b7c15ed9d9a diff --git a/code/mobile/android/PhoneVR/app/build.gradle b/code/mobile/android/PhoneVR/app/build.gradle index 399bcf30..36b9f363 100644 --- a/code/mobile/android/PhoneVR/app/build.gradle +++ b/code/mobile/android/PhoneVR/app/build.gradle @@ -207,14 +207,21 @@ task deleteNdk(type: Delete) { delete "../ALVR/target/" } +task deleteGradleJniCache(type: Delete) +{ + // delete merged_jni_libs + delete "build/intermediates/merged_jni_libs" +} + task buildClientLib { + dependsOn deleteGradleJniCache doLast { exec { workingDir '../ALVR' if(defaultALVRFlavour.length() > 0) - commandLine 'cargo', 'xtask', 'build-client-lib', '--no-stdcpp', defaultALVRFlavour + commandLine 'cargo', 'xtask', 'build-client-lib', '--all-targets', '--no-stdcpp', defaultALVRFlavour else - commandLine 'cargo', 'xtask', 'build-client-lib', '--no-stdcpp' + commandLine 'cargo', 'xtask', 'build-client-lib', '--all-targets', '--no-stdcpp' } } } diff --git a/code/mobile/android/PhoneVR/app/src/main/AndroidManifest.xml b/code/mobile/android/PhoneVR/app/src/main/AndroidManifest.xml index c193327f..cec5caaf 100644 --- a/code/mobile/android/PhoneVR/app/src/main/AndroidManifest.xml +++ b/code/mobile/android/PhoneVR/app/src/main/AndroidManifest.xml @@ -12,6 +12,7 @@ + #include #include #include @@ -44,18 +45,39 @@ struct NativeContext { GLuint lobbyTextures[2] = {0, 0}; GLuint streamTextures[2] = {0, 0}; + EGLContext eglCurrentContext = nullptr; + EGLSurface eglCurrentDrawSurface = nullptr; + EGLSurface eglCurrentReadSurface = nullptr; + EGLDisplay eglCurrentDisplay = nullptr; + bool cachedEglConfig = false; + float eyeOffsets[2] = {0.0, 0.0}; AlvrFov fovArr[2] = {}; AlvrViewParams viewParams[2] = {}; + AlvrDeviceMotion deviceMotion = {}; + AlvrQuat recentReprojectionRotation = { + 0, + 0, + 0, + 1, + }; + + // ALVR StreamingStarted event's config + StreamingStarted_Body streamingConfig; + AlvrDecoderConfig decoderConfig; NativeContext() { - memset(&fovArr, 0, (sizeof(fovArr)) / sizeof(int)); - memset(&viewParams, 0, (sizeof(viewParams)) / sizeof(int)); - memset(&deviceMotion, 0, (sizeof(deviceMotion)) / sizeof(int)); + memset(&fovArr, 0, sizeof(fovArr)); + memset(&deviceMotion, 0, sizeof(deviceMotion)); + memset(&streamingConfig, 0, sizeof(streamingConfig)); + memset(&decoderConfig, 0, sizeof(decoderConfig)); + memset(&viewParams, 0, sizeof(viewParams)); } }; +void initialize_decoder(AlvrDecoderConfig config); + NativeContext CTX = {}; int64_t GetBootTimeNano() { @@ -105,7 +127,10 @@ AlvrFov getFov(CardboardEye eye) { return fov; } -AlvrPose getPose(uint64_t timestampNs) { +AlvrPose getPose(uint64_t timestampNs = 0) { + if (!timestampNs) + timestampNs = GetBootTimeNano(); + AlvrPose pose = {}; float pos[3]; @@ -118,24 +143,30 @@ AlvrPose getPose(uint64_t timestampNs) { return pose; } -void updateViewConfigs(uint64_t targetTimestampNs = 0) { +void updateHeadPose(uint64_t targetTimestampNs = 0) { if (!targetTimestampNs) - targetTimestampNs = GetBootTimeNano() + alvr_get_head_prediction_offset_ns(); + targetTimestampNs = GetBootTimeNano(); AlvrPose headPose = getPose(targetTimestampNs); CTX.deviceMotion.device_id = HEAD_ID; CTX.deviceMotion.pose = headPose; +} + +void updateViewConfigs(uint64_t targetTimestampNs = 0) { + updateHeadPose(targetTimestampNs); float headToEye[3] = {CTX.eyeOffsets[kLeft], 0.0, 0.0}; - CTX.viewParams[kLeft].pose = headPose; - offsetPosWithQuat(headPose.orientation, headToEye, CTX.viewParams[kLeft].pose.position); + CTX.viewParams[kLeft].pose = CTX.deviceMotion.pose; + offsetPosWithQuat( + CTX.deviceMotion.pose.orientation, headToEye, CTX.viewParams[kLeft].pose.position); CTX.viewParams[kLeft].fov = CTX.fovArr[kLeft]; headToEye[0] = CTX.eyeOffsets[kRight]; - CTX.viewParams[kRight].pose = headPose; - offsetPosWithQuat(headPose.orientation, headToEye, CTX.viewParams[kRight].pose.position); + CTX.viewParams[kRight].pose = CTX.deviceMotion.pose; + offsetPosWithQuat( + CTX.deviceMotion.pose.orientation, headToEye, CTX.viewParams[kRight].pose.position); CTX.viewParams[kRight].fov = CTX.fovArr[kRight]; } @@ -145,12 +176,14 @@ void inputThread() { info("inputThread: thread staring..."); while (CTX.streaming) { - auto targetTimestampNs = GetBootTimeNano() + alvr_get_head_prediction_offset_ns(); - updateViewConfigs(targetTimestampNs); + auto targetTimestampNs = GetBootTimeNano(); + updateHeadPose(targetTimestampNs); - alvr_send_tracking( - targetTimestampNs, CTX.viewParams, &CTX.deviceMotion, 1, nullptr, nullptr); + memcpy( + &CTX.recentReprojectionRotation, &CTX.deviceMotion.pose.orientation, sizeof(AlvrQuat)); + alvr_send_tracking(targetTimestampNs, &CTX.deviceMotion, 1, nullptr, nullptr); + // Todo: Adapt to the Screen's framerate not 60 deadline += std::chrono::nanoseconds((uint64_t) (1e9 / 60.f / 3)); std::this_thread::sleep_until(deadline); } @@ -161,6 +194,10 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *) { return JNI_VERSION_1_6; } +AlvrQuat getReprojectionRotationDelta() { + return alvr_rotation_delta(CTX.recentReprojectionRotation, getPose().orientation); +} + extern "C" JNIEXPORT void JNICALL Java_viritualisres_phonevr_ALVRActivity_initializeNative( JNIEnv *env, jobject obj, jint screenWidth, jint screenHeight, jfloat refreshRate) { CTX.javaContext = env->NewGlobalRef(obj); @@ -170,12 +207,12 @@ extern "C" JNIEXPORT void JNICALL Java_viritualisres_phonevr_ALVRActivity_initia alvr_initialize_android_context((void *) CTX.javaVm, (void *) CTX.javaContext); + alvr_initialize_logging(); float refreshRatesBuffer[1] = {refreshRate}; AlvrClientCapabilities caps = {}; caps.default_view_height = viewHeight; caps.default_view_width = viewWidth; - caps.external_decoder = false; caps.refresh_rates = refreshRatesBuffer; caps.refresh_rates_count = 1; caps.foveated_encoding = @@ -190,9 +227,30 @@ extern "C" JNIEXPORT void JNICALL Java_viritualisres_phonevr_ALVRActivity_initia CTX.headTracker = CardboardHeadTracker_create(); } +void initialize_decoder(AlvrDecoderConfig config) { alvr_create_decoder(config); } + +void makeGLContextCurrent() { + if (!CTX.cachedEglConfig) { + CTX.eglCurrentContext = eglGetCurrentContext(); + CTX.eglCurrentDrawSurface = eglGetCurrentSurface(EGL_DRAW); + CTX.eglCurrentDisplay = eglGetCurrentDisplay(); + CTX.eglCurrentReadSurface = eglGetCurrentSurface(EGL_READ); + CTX.cachedEglConfig = true; + } + + GL(eglMakeCurrent(CTX.eglCurrentDisplay, + CTX.eglCurrentDrawSurface, + CTX.eglCurrentReadSurface, + CTX.eglCurrentContext)); + info("eglMakeCurrent() returned error %s", eglGetErrorString()); +} + extern "C" JNIEXPORT void JNICALL Java_viritualisres_phonevr_ALVRActivity_destroyNative(JNIEnv *, jobject) { - alvr_destroy_opengl(); + EGL_MAKE_CURRENT(alvr_destroy_opengl()); + CTX.cachedEglConfig = false; + + alvr_destroy_decoder(); alvr_destroy(); CardboardHeadTracker_destroy(CTX.headTracker); @@ -235,7 +293,8 @@ extern "C" JNIEXPORT void JNICALL Java_viritualisres_phonevr_ALVRActivity_pauseN extern "C" JNIEXPORT void JNICALL Java_viritualisres_phonevr_ALVRActivity_surfaceCreatedNative(JNIEnv *, jobject) { - alvr_initialize_opengl(); + + EGL_MAKE_CURRENT(alvr_initialize_opengl()); CTX.glContextRecreated = true; } @@ -301,14 +360,15 @@ extern "C" JNIEXPORT void JNICALL Java_viritualisres_phonevr_ALVRActivity_render CTX.fovArr[kLeft] = getFov(kLeft); CTX.fovArr[kRight] = getFov(kRight); + updateViewConfigs(); info("renderingParamsChanged, updating new view configs (FOV) to alvr"); - // alvr_send_views_config(fovArr, CTX.eyeOffsets[0] - CTX.eyeOffsets[1]); + alvr_send_view_params(CTX.viewParams); } // Note: if GL context is recreated, old resources are already freed. if (CTX.renderingParamsChanged && !CTX.glContextRecreated) { info("Pausing ALVR since glContext is not recreated, deleting textures"); - alvr_pause_opengl(); + EGL_MAKE_CURRENT(alvr_pause_opengl()); GL(glDeleteTextures(2, CTX.lobbyTextures)); } @@ -328,18 +388,20 @@ extern "C" JNIEXPORT void JNICALL Java_viritualisres_phonevr_ALVRActivity_render GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); GL(glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGB, + GL_RGBA8, CTX.screenWidth / 2, CTX.screenHeight, 0, - GL_RGB, + GL_RGBA, GL_UNSIGNED_BYTE, nullptr)); } const uint32_t *targetViews[2] = {(uint32_t *) &CTX.lobbyTextures[0], (uint32_t *) &CTX.lobbyTextures[1]}; - alvr_resume_opengl(CTX.screenWidth / 2, CTX.screenHeight, targetViews, 1, true); + + EGL_MAKE_CURRENT( + alvr_resume_opengl(CTX.screenWidth / 2, CTX.screenHeight, targetViews, 1)); CTX.renderingParamsChanged = false; CTX.glContextRecreated = false; @@ -354,13 +416,14 @@ extern "C" JNIEXPORT void JNICALL Java_viritualisres_phonevr_ALVRActivity_render alvr_hud_message(&message_buffer[0]); info("ALVR Poll Event: HUD Message Update - %s", &message_buffer[0]); - if (message_length > 0) - alvr_update_hud_message_opengl(&message_buffer[0]); + if (message_length > 0) { + EGL_MAKE_CURRENT(alvr_update_hud_message_opengl(&message_buffer[0])); + } } if (event.tag == ALVR_EVENT_STREAMING_STARTED) { info("ALVR Poll Event: ALVR_EVENT_STREAMING_STARTED, generating and binding " "textures..."); - auto config = event.STREAMING_STARTED; + CTX.streamingConfig = event.STREAMING_STARTED; auto settings_len = alvr_get_settings_json(nullptr); auto settings_buffer = std::vector(settings_len); @@ -382,8 +445,8 @@ extern "C" JNIEXPORT void JNICALL Java_viritualisres_phonevr_ALVRActivity_render GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, - config.view_width, - config.view_height, + CTX.streamingConfig.view_width, + CTX.streamingConfig.view_height, 0, GL_RGB, GL_UNSIGNED_BYTE, @@ -400,13 +463,29 @@ extern "C" JNIEXPORT void JNICALL Java_viritualisres_phonevr_ALVRActivity_render const uint32_t *textureHandles[2] = {&leftIntHandle, &rightIntHandle}; auto render_config = AlvrStreamConfig{}; - render_config.view_resolution_width = config.view_width; - render_config.view_resolution_height = config.view_height; + render_config.view_resolution_width = CTX.streamingConfig.view_width; + render_config.view_resolution_height = CTX.streamingConfig.view_height; render_config.swapchain_textures = textureHandles; render_config.swapchain_length = 1; render_config.enable_foveation = false; if (!settings_json["video"].is_null()) { + CTX.decoderConfig.force_software_decoder = + settings_json["video"]["force_software_decoder"]; + CTX.decoderConfig.buffering_history_weight = + settings_json["video"]["buffering_history_weight"]; + CTX.decoderConfig.max_buffering_frames = + settings_json["video"]["max_buffering_frames"]; + + info("settings_json[video][force_software_decoder] is %s ", + settings_json["video"]["force_software_decoder"].dump().c_str()); + info("settings_json[video][buffering_history_weight] is %s ", + settings_json["video"]["buffering_history_weight"].dump().c_str()); + info("settings_json[video][max_buffering_frames] is %s ", + settings_json["video"]["max_buffering_frames"].dump().c_str()); + info("settings_json[video][mediacodec_extra_options] is %s ", + settings_json["video"]["mediacodec_extra_options"].dump().c_str()); + if (!settings_json["video"]["foveated_encoding"].is_null()) { info("settings_json.video.foveated_encoding is %s", settings_json["video"]["foveated_encoding"].dump().c_str()); @@ -454,7 +533,7 @@ extern "C" JNIEXPORT void JNICALL Java_viritualisres_phonevr_ALVRActivity_render info("render_config.foveation_edge_ratio_y: %f", render_config.foveation_edge_ratio_y); - alvr_start_stream_opengl(render_config); + EGL_MAKE_CURRENT(alvr_start_stream_opengl(render_config)); info("ALVR Poll Event: ALVR_EVENT_STREAMING_STARTED, opengl stream started and " "input " @@ -471,6 +550,10 @@ extern "C" JNIEXPORT void JNICALL Java_viritualisres_phonevr_ALVRActivity_render GL(glDeleteTextures(2, CTX.streamTextures)); info("ALVR Poll Event: ALVR_EVENT_STREAMING_STOPPED, Stream stopped deleted " "textures."); + } else if (event.tag == ALVR_EVENT_DECODER_CONFIG) { + info("ALVR Poll Event: ALVR_EVENT_DECODER_CONFIG, "); + CTX.decoderConfig.codec = event.DECODER_CONFIG.codec; + initialize_decoder(CTX.decoderConfig); } } @@ -485,43 +568,41 @@ extern "C" JNIEXPORT void JNICALL Java_viritualisres_phonevr_ALVRActivity_render if (CTX.streaming) { void *streamHardwareBuffer = nullptr; - AlvrViewParams dummyViewParams; - auto timestampNs = alvr_get_frame(&dummyViewParams, &streamHardwareBuffer); + uint64_t timestampNs; + alvr_get_frame(×tampNs, &streamHardwareBuffer); - if (timestampNs == -1) { - return; - } + const AlvrStreamViewParams streamViewParams[2] = { + {0, getReprojectionRotationDelta(), CTX.fovArr[kLeft]}, + {0, getReprojectionRotationDelta(), CTX.fovArr[kRight]}, + }; - uint32_t swapchainIndices[2] = {0, 0}; - alvr_render_stream_opengl(streamHardwareBuffer, swapchainIndices); + EGL_MAKE_CURRENT(alvr_render_stream_opengl(streamHardwareBuffer, streamViewParams)); alvr_report_submit(timestampNs, 0); viewsDescs[0].texture = CTX.streamTextures[0]; viewsDescs[1].texture = CTX.streamTextures[1]; } else { + info("Getting pose for Rendering Lobby..."); AlvrPose pose = getPose(GetBootTimeNano() + VSYNC_QUEUE_INTERVAL_NS); - AlvrViewInput viewInputs[2] = {}; + AlvrLobbyViewParams viewInputs[2] = {}; for (int eye = 0; eye < 2; eye++) { float headToEye[3] = {CTX.eyeOffsets[eye], 0.0, 0.0}; // offset head pos to Eye Position offsetPosWithQuat(pose.orientation, headToEye, viewInputs[eye].pose.position); + viewInputs[eye].swapchain_index = 0; viewInputs[eye].pose.orientation = pose.orientation; viewInputs[eye].fov = getFov((CardboardEye) eye); - viewInputs[eye].swapchain_index = 0; } - alvr_render_lobby_opengl(viewInputs); + info("Rendering Lobby..."); + EGL_MAKE_CURRENT(alvr_render_lobby_opengl(viewInputs, true)); viewsDescs[0].texture = CTX.lobbyTextures[0]; viewsDescs[1].texture = CTX.lobbyTextures[1]; } - // Note: the Cardboard SDK does not support reprojection! - // todo: manually implement it? - - // info("nativeRendered: Rendering to Display..."); CardboardDistortionRenderer_renderEyeToDisplay(CTX.distortionRenderer, 0, 0, @@ -530,6 +611,8 @@ extern "C" JNIEXPORT void JNICALL Java_viritualisres_phonevr_ALVRActivity_render CTX.screenHeight, &viewsDescs[0], &viewsDescs[1]); + + info("Rendered to Display"); } catch (const json::exception &e) { error(std::string(std::string(__FUNCTION__) + std::string(__FILE_NAME__) + std::string(e.what())) diff --git a/code/mobile/android/PhoneVR/app/src/main/cpp/utils.h b/code/mobile/android/PhoneVR/app/src/main/cpp/utils.h index bc31f22b..0830329e 100644 --- a/code/mobile/android/PhoneVR/app/src/main/cpp/utils.h +++ b/code/mobile/android/PhoneVR/app/src/main/cpp/utils.h @@ -34,21 +34,6 @@ void log(AlvrLogLevel level, alvr_log(level, buf); - switch (level) { - case ALVR_LOG_LEVEL_DEBUG: - __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "%s", buf); - break; - case ALVR_LOG_LEVEL_INFO: - __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "%s", buf); - break; - case ALVR_LOG_LEVEL_ERROR: - __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "%s", buf); - break; - case ALVR_LOG_LEVEL_WARN: - __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "%s", buf); - break; - } - va_end(args); } @@ -87,4 +72,36 @@ static const char *GlErrorString(GLenum error) { func; \ GLCheckErrors(__FILE__, __LINE__) +#define CASE_STR(value) \ + case value: \ + return #value; +const char *eglGetErrorString() { + EGLint error = eglGetError(); + switch (error) { + CASE_STR(EGL_SUCCESS) + CASE_STR(EGL_NOT_INITIALIZED) + CASE_STR(EGL_BAD_ACCESS) + CASE_STR(EGL_BAD_ALLOC) + CASE_STR(EGL_BAD_ATTRIBUTE) + CASE_STR(EGL_BAD_CONTEXT) + CASE_STR(EGL_BAD_CONFIG) + CASE_STR(EGL_BAD_CURRENT_SURFACE) + CASE_STR(EGL_BAD_DISPLAY) + CASE_STR(EGL_BAD_SURFACE) + CASE_STR(EGL_BAD_MATCH) + CASE_STR(EGL_BAD_PARAMETER) + CASE_STR(EGL_BAD_NATIVE_PIXMAP) + CASE_STR(EGL_BAD_NATIVE_WINDOW) + CASE_STR(EGL_CONTEXT_LOST) + default: + return "Unknown"; + } +} +#undef CASE_STR + +void makeGLContextCurrent(); + +#define EGL_MAKE_CURRENT(func) func; +// makeGLContextCurrent() + #endif // PHONEVR_UTILS_H diff --git a/code/mobile/android/PhoneVR/gradle/wrapper/gradle-wrapper.properties b/code/mobile/android/PhoneVR/gradle/wrapper/gradle-wrapper.properties index 5ef3a45b..adc0d415 100644 --- a/code/mobile/android/PhoneVR/gradle/wrapper/gradle-wrapper.properties +++ b/code/mobile/android/PhoneVR/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Tue Apr 25 03:20:28 IST 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists