diff --git a/android-cmake b/android-cmake index 6855fd1..34962ef 160000 --- a/android-cmake +++ b/android-cmake @@ -1 +1 @@ -Subproject commit 6855fd151acad2ccbacf9b31d9af11ac68c74317 +Subproject commit 34962efa6bb0f897ce6664e4aa9b1f065609ceb7 diff --git a/src/OSVR-Core b/src/OSVR-Core index c871b01..df7eae9 160000 --- a/src/OSVR-Core +++ b/src/OSVR-Core @@ -1 +1 @@ -Subproject commit c871b0152c50b317d875a23a5a2bdb5e6cc44d0f +Subproject commit df7eae9a0dc1c90a3b00416697b7a98b47bc47a0 diff --git a/src/OSVR-RenderManager b/src/OSVR-RenderManager index 4fb158a..d6d0f04 160000 --- a/src/OSVR-RenderManager +++ b/src/OSVR-RenderManager @@ -1 +1 @@ -Subproject commit 4fb158a4538a2f68407211ed7abd2c5dbe61730b +Subproject commit d6d0f041124e1def154e534228dbb89c44241e6f diff --git a/src/OSVR-Unity-Rendering/OsvrRenderingPlugin.cpp b/src/OSVR-Unity-Rendering/OsvrRenderingPlugin.cpp index 9a34069..4e5b291 100644 --- a/src/OSVR-Unity-Rendering/OsvrRenderingPlugin.cpp +++ b/src/OSVR-Unity-Rendering/OsvrRenderingPlugin.cpp @@ -50,6 +50,12 @@ Sensics, Inc. #include #include +#if OSVR_ANDROID +#include +#include +#include +#endif + // VARIABLES static IUnityInterfaces *s_UnityInterfaces = nullptr; static IUnityGraphics *s_Graphics = nullptr; @@ -283,11 +289,60 @@ static void checkGlError(const char *op) { } } +#if OSVR_ANDROID +static long gPreviousFrameTimeNanos = 0; +static long gLastFrameTimeNanos = 0; +static std::mutex gFrameTimeMutex; + +static void frameCallbackImpl(long frameTimeNanos, void *data) { + std::lock_guard lockGuard(gFrameTimeMutex); + gPreviousFrameTimeNanos = gLastFrameTimeNanos; + gLastFrameTimeNanos = frameTimeNanos; + //LOGI("frameTimeNanos: %d", frameTimeNanos); + //LOGI("diff: %d", frameTimeNanos - gPreviousFrameTimeNanos); + AChoreographer* choreographer = AChoreographer_getInstance(); + if(!choreographer) { + LOGE("Couldn't get the choreographer from the frame callback"); + } + AChoreographer_postFrameCallback(choreographer, frameCallbackImpl, nullptr); +} + +static void choreographerThreadRun() { + ALooper* looper = ALooper_forThread(); + if(!looper) { + looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); + if(!looper) { + LOGE("Couldn't prepare an ALooper for the choreographer thread"); + return; + } + } + + AChoreographer* choreographer = AChoreographer_getInstance(); + if(!choreographer) { + LOGE("Could not get choreographer."); + return; + } + + AChoreographer_postFrameCallback(choreographer, frameCallbackImpl, nullptr); + + while(true) { + int fd_unused = 0; + int outEvents_unused = 0; + void *outData_unused = nullptr; + int rc = ALooper_pollAll(100, &fd_unused, &outEvents_unused, &outData_unused); + } +} +#endif + class PassThroughOpenGLContextImpl { OSVR_OpenGLToolkitFunctions toolkit; int mWidth; int mHeight; +#if OSVR_ANDROID + std::thread mChoreographerThread; +#endif + static void createImpl(void* data) { } static void destroyImpl(void* data) { @@ -317,10 +372,16 @@ class PassThroughOpenGLContextImpl { static OSVR_CBool getDisplaySizeOverrideImpl(void* data, size_t display, int* width, int* height) { return ((PassThroughOpenGLContextImpl*)data)->getDisplaySizeOverride(display, width, height); } - + static OSVR_CBool getRenderTimingInfoImpl(void* data, size_t display, size_t whichEye, OSVR_RenderTimingInfo* renderTimingInfoOut) { + return ((PassThroughOpenGLContextImpl*)data)->getRenderTimingInfo(display, whichEye, renderTimingInfoOut); + } public: +#if OSVR_ANDROID + PassThroughOpenGLContextImpl() : mChoreographerThread(choreographerThreadRun) { +#else PassThroughOpenGLContextImpl() { +#endif memset(&toolkit, 0, sizeof(toolkit)); toolkit.size = sizeof(toolkit); toolkit.data = this; @@ -335,6 +396,7 @@ class PassThroughOpenGLContextImpl { toolkit.handleEvents = handleEventsImpl; toolkit.getDisplaySizeOverride = getDisplaySizeOverrideImpl; toolkit.getDisplayFrameBuffer = getDisplayFrameBufferImpl; + toolkit.getRenderTimingInfo = getRenderTimingInfoImpl; } ~PassThroughOpenGLContextImpl() { @@ -375,6 +437,31 @@ class PassThroughOpenGLContextImpl { *height = gHeight; return false; } + + bool getRenderTimingInfo(size_t display, size_t whichEye, OSVR_RenderTimingInfo* renderTimingInfoOut) { +#if OSVR_ANDROID + timespec tp; + int rv = clock_gettime(CLOCK_MONOTONIC, &tp); + if(!rv) { + std::lock_guard lockGuard(gFrameTimeMutex); + //uint64_t presentationDeadline = 17683333L; // Shouldn't this bee at least less than 16,666,666? (1 billion / 60) + // that's 1,016,666 nanoseconds greater than the hardware interval + //uint64_t hardwareDisplayIntervalNanos = 11111111L; // 90Hz hard coded, TODO measure this or get it from the API? + uint64_t hardwareDisplayIntervalNanos = gLastFrameTimeNanos - gPreviousFrameTimeNanos; + uint64_t nowNanos = ((tp.tv_sec * 1000000000L) + tp.tv_nsec); + uint64_t timeFromLastNanos = nowNanos >= gLastFrameTimeNanos ? nowNanos - gLastFrameTimeNanos : 0; // just being safe + uint64_t nextNanos = gLastFrameTimeNanos + hardwareDisplayIntervalNanos; + uint64_t timeUntilNextNanos = nextNanos >= nowNanos ? nextNanos - nowNanos : 0; // 0 here means we missed a vsync + + nanoSecondsToTimeValue(timeFromLastNanos, &renderTimingInfoOut->timeSincelastVerticalRetrace); + nanoSecondsToTimeValue(timeUntilNextNanos, &renderTimingInfoOut->timeUntilNextPresentRequired); + nanoSecondsToTimeValue(hardwareDisplayIntervalNanos, &renderTimingInfoOut->hardwareDisplayInterval); + return true; + } + LOGI("clock_gettime call failed."); +#endif + return false; + } }; static GLuint loadShader(GLenum shaderType, const char *pSource) {