From 791352ffe3bd036f9e8eddd3ff49ee84a1eb572a Mon Sep 17 00:00:00 2001 From: "r.kuznetsov" Date: Thu, 14 Jun 2018 17:25:09 +0300 Subject: [PATCH] Added Android OGL context validation and present availability --- android/jni/com/mapswithme/maps/Framework.cpp | 12 +- .../mapswithme/opengl/androidoglcontext.cpp | 16 +- .../mapswithme/opengl/androidoglcontext.hpp | 8 +- .../opengl/androidoglcontextfactory.cpp | 7 +- .../opengl/androidoglcontextfactory.hpp | 11 +- drape/oglcontext.hpp | 8 +- drape/oglcontextfactory.cpp | 12 +- drape/oglcontextfactory.hpp | 6 +- drape_frontend/backend_renderer.cpp | 5 +- drape_frontend/frontend_renderer.cpp | 140 ++++++++++-------- iphone/Maps/Classes/EAGLView.mm | 2 +- iphone/Maps/Classes/iosOGLContext.h | 11 +- iphone/Maps/Classes/iosOGLContextFactory.h | 2 +- 13 files changed, 141 insertions(+), 99 deletions(-) diff --git a/android/jni/com/mapswithme/maps/Framework.cpp b/android/jni/com/mapswithme/maps/Framework.cpp index d96ae1cbef..352e1c0ff9 100644 --- a/android/jni/com/mapswithme/maps/Framework.cpp +++ b/android/jni/com/mapswithme/maps/Framework.cpp @@ -183,7 +183,10 @@ void Framework::Resize(int w, int h) void Framework::DetachSurface(bool destroyContext) { - LOG(LINFO, ("Detach surface.")); + LOG(LINFO, ("Detach surface started. destroyContext =", destroyContext)); + ASSERT(m_contextFactory != nullptr, ()); + m_contextFactory->setPresentAvailable(false); + if (destroyContext) { LOG(LINFO, ("Destroy context.")); @@ -193,14 +196,14 @@ void Framework::DetachSurface(bool destroyContext) } m_work.SetRenderingDisabled(destroyContext); - ASSERT(m_contextFactory != nullptr, ()); AndroidOGLContextFactory * factory = m_contextFactory->CastFactory(); factory->ResetSurface(); + LOG(LINFO, ("Detach surface finished.")); } bool Framework::AttachSurface(JNIEnv * env, jobject jSurface) { - LOG(LINFO, ("Attach surface.")); + LOG(LINFO, ("Attach surface started.")); ASSERT(m_contextFactory != nullptr, ()); AndroidOGLContextFactory * factory = m_contextFactory->CastFactory(); @@ -214,6 +217,7 @@ bool Framework::AttachSurface(JNIEnv * env, jobject jSurface) ASSERT(!m_guiPositions.empty(), ("GUI elements must be set-up before engine is created")); + m_contextFactory->setPresentAvailable(true); m_work.SetRenderingEnabled(factory); if (m_isContextDestroyed) @@ -225,6 +229,8 @@ bool Framework::AttachSurface(JNIEnv * env, jobject jSurface) m_work.EnterForeground(); } + LOG(LINFO, ("Attach surface finished.")); + return true; } diff --git a/android/jni/com/mapswithme/opengl/androidoglcontext.cpp b/android/jni/com/mapswithme/opengl/androidoglcontext.cpp index 09fd588b81..e80133df4f 100644 --- a/android/jni/com/mapswithme/opengl/androidoglcontext.cpp +++ b/android/jni/com/mapswithme/opengl/androidoglcontext.cpp @@ -26,6 +26,7 @@ AndroidOGLContext::AndroidOGLContext(bool supportedES3, EGLDisplay display, EGLS : m_nativeContext(EGL_NO_CONTEXT) , m_surface(surface) , m_display(display) + , m_presentAvailable(true) { ASSERT(m_surface != EGL_NO_SURFACE, ()); ASSERT(m_display != EGL_NO_DISPLAY, ()); @@ -73,8 +74,22 @@ void AndroidOGLContext::setRenderingEnabled(bool enabled) clearCurrent(); } +void AndroidOGLContext::setPresentAvailable(bool available) +{ + m_presentAvailable = available; +} + +bool AndroidOGLContext::validate() +{ + return eglGetCurrentDisplay() != EGL_NO_DISPLAY && + eglGetCurrentSurface(EGL_DRAW) != EGL_NO_SURFACE && + eglGetCurrentContext() != EGL_NO_CONTEXT; +} + void AndroidOGLContext::present() { + if (!m_presentAvailable) + return; ASSERT(m_surface != EGL_NO_SURFACE, ()); if (eglSwapBuffers(m_display, m_surface) == EGL_FALSE) CHECK_EGL_CALL(); @@ -90,5 +105,4 @@ void AndroidOGLContext::resetSurface() { m_surface = EGL_NO_SURFACE; } - } // namespace android diff --git a/android/jni/com/mapswithme/opengl/androidoglcontext.hpp b/android/jni/com/mapswithme/opengl/androidoglcontext.hpp index 5c0bed7765..839030cd39 100644 --- a/android/jni/com/mapswithme/opengl/androidoglcontext.hpp +++ b/android/jni/com/mapswithme/opengl/androidoglcontext.hpp @@ -3,9 +3,10 @@ #include "drape/glIncludes.hpp" #include "drape/oglcontext.hpp" +#include + namespace android { - class AndroidOGLContext : public dp::OGLContext { public: @@ -18,6 +19,8 @@ public: void present() override; void setDefaultFramebuffer() override; void setRenderingEnabled(bool enabled) override; + void setPresentAvailable(bool available) override; + bool validate() override; void setSurface(EGLSurface surface); void resetSurface(); @@ -33,6 +36,7 @@ private: EGLSurface m_surface; EGLDisplay m_display; // @} -}; + std::atomic m_presentAvailable; +}; } // namespace android diff --git a/android/jni/com/mapswithme/opengl/androidoglcontextfactory.cpp b/android/jni/com/mapswithme/opengl/androidoglcontextfactory.cpp index 73dae65886..8f7017f373 100644 --- a/android/jni/com/mapswithme/opengl/androidoglcontextfactory.cpp +++ b/android/jni/com/mapswithme/opengl/androidoglcontextfactory.cpp @@ -274,6 +274,12 @@ bool AndroidOGLContextFactory::isUploadContextCreated() const return m_uploadContext != nullptr; } +void AndroidOGLContextFactory::setPresentAvailable(bool available) +{ + if (m_drawContext != nullptr) + m_drawContext->setPresentAvailable(available); +} + bool AndroidOGLContextFactory::createWindowSurface() { EGLConfig configs[kMaxConfigCount]; @@ -341,5 +347,4 @@ bool AndroidOGLContextFactory::createPixelbufferSurface() return true; } - } // namespace android diff --git a/android/jni/com/mapswithme/opengl/androidoglcontextfactory.hpp b/android/jni/com/mapswithme/opengl/androidoglcontextfactory.hpp index ed6d73cd8a..a86caea5a4 100644 --- a/android/jni/com/mapswithme/opengl/androidoglcontextfactory.hpp +++ b/android/jni/com/mapswithme/opengl/androidoglcontextfactory.hpp @@ -8,7 +8,6 @@ namespace android { - class AndroidOGLContextFactory : public dp::OGLContextFactory { public: @@ -17,10 +16,11 @@ public: bool IsValid() const; - virtual dp::OGLContext * getDrawContext(); - virtual dp::OGLContext * getResourcesUploadContext(); - virtual bool isDrawContextCreated() const; - virtual bool isUploadContextCreated() const; + dp::OGLContext * getDrawContext() override; + dp::OGLContext * getResourcesUploadContext() override; + bool isDrawContextCreated() const override; + bool isUploadContextCreated() const override; + void setPresentAvailable(bool available) override; void SetSurface(JNIEnv * env, jobject jsurface); void ResetSurface(); @@ -54,5 +54,4 @@ private: bool m_windowSurfaceValid; bool m_supportedES3; }; - } // namespace android diff --git a/drape/oglcontext.hpp b/drape/oglcontext.hpp index 9c55ff1345..baaa5bcc3a 100644 --- a/drape/oglcontext.hpp +++ b/drape/oglcontext.hpp @@ -2,7 +2,6 @@ namespace dp { - class OGLContext { public: @@ -11,9 +10,10 @@ public: virtual void makeCurrent() = 0; virtual void doneCurrent() {} virtual void setDefaultFramebuffer() = 0; - /// @ param w, h - pixel size of render target (logical size * visual scale) + // w, h - pixel size of render target (logical size * visual scale). virtual void resize(int /*w*/, int /*h*/) {} virtual void setRenderingEnabled(bool /*enabled*/) {} + virtual void setPresentAvailable(bool /*available*/) {} + virtual bool validate() { return true; } }; - -} // namespace dp +} // namespace dp diff --git a/drape/oglcontextfactory.cpp b/drape/oglcontextfactory.cpp index 5427564cc7..da61f0f311 100644 --- a/drape/oglcontextfactory.cpp +++ b/drape/oglcontextfactory.cpp @@ -2,12 +2,10 @@ namespace dp { - ThreadSafeFactory::ThreadSafeFactory(OGLContextFactory * factory, bool enableSharing) : m_factory(factory) , m_enableSharing(enableSharing) -{ -} +{} ThreadSafeFactory::~ThreadSafeFactory() { @@ -45,5 +43,9 @@ void ThreadSafeFactory::waitForInitialization(dp::OGLContext * context) { m_factory->waitForInitialization(context); } - -} // namespace dp + +void ThreadSafeFactory::setPresentAvailable(bool available) +{ + m_factory->setPresentAvailable(available); +} +} // namespace dp diff --git a/drape/oglcontextfactory.hpp b/drape/oglcontextfactory.hpp index 4ad52b3870..cf0438bbad 100644 --- a/drape/oglcontextfactory.hpp +++ b/drape/oglcontextfactory.hpp @@ -9,7 +9,6 @@ namespace dp { - class OGLContextFactory { public: @@ -19,6 +18,7 @@ public: virtual bool isDrawContextCreated() const { return false; } virtual bool isUploadContextCreated() const { return false; } virtual void waitForInitialization(dp::OGLContext * context) {} + virtual void setPresentAvailable(bool available) {} }; class ThreadSafeFactory : public OGLContextFactory @@ -37,6 +37,7 @@ public: } void waitForInitialization(dp::OGLContext * context) override; + void setPresentAvailable(bool available) override; protected: typedef function TCreateCtxFn; @@ -48,5 +49,4 @@ private: threads::Condition m_contidion; bool m_enableSharing; }; - -} // namespace dp +} // namespace dp diff --git a/drape_frontend/backend_renderer.cpp b/drape_frontend/backend_renderer.cpp index 2ff33c5092..bd775275ed 100644 --- a/drape_frontend/backend_renderer.cpp +++ b/drape_frontend/backend_renderer.cpp @@ -557,10 +557,11 @@ void BackendRenderer::Routine::Do() { LOG(LINFO, ("Start routine.")); m_renderer.OnContextCreate(); - + dp::OGLContext * context = m_renderer.m_contextFactory->getResourcesUploadContext(); while (!IsCancelled()) { - m_renderer.ProcessSingleMessage(); + if (context->validate()) + m_renderer.ProcessSingleMessage(); m_renderer.CheckRenderingEnabled(); } diff --git a/drape_frontend/frontend_renderer.cpp b/drape_frontend/frontend_renderer.cpp index 62a88aea9b..f5053eaf73 100755 --- a/drape_frontend/frontend_renderer.cpp +++ b/drape_frontend/frontend_renderer.cpp @@ -1922,83 +1922,95 @@ void FrontendRenderer::Routine::Do() double frameTime = 0.0; bool modelViewChanged = true; bool viewportChanged = true; + bool invalidContext = false; dp::OGLContext * context = m_renderer.m_contextFactory->getDrawContext(); while (!IsCancelled()) { - ScreenBase modelView = m_renderer.ProcessEvents(modelViewChanged, viewportChanged); - if (viewportChanged) - m_renderer.OnResize(modelView); - - // Check for a frame is active. - bool isActiveFrame = modelViewChanged || viewportChanged; - - if (isActiveFrame) - m_renderer.PrepareScene(modelView); - - isActiveFrame |= m_renderer.m_myPositionController->IsWaitingForTimers(); - isActiveFrame |= m_renderer.m_texMng->UpdateDynamicTextures(); - m_renderer.m_routeRenderer->UpdatePreview(modelView); - - m_renderer.RenderScene(modelView); - - if (modelViewChanged || m_renderer.m_forceUpdateScene || m_renderer.m_forceUpdateUserMarks) - m_renderer.UpdateScene(modelView); - - isActiveFrame |= InterpolationHolder::Instance().Advance(frameTime); - AnimationSystem::Instance().Advance(frameTime); - - isActiveFrame |= m_renderer.m_userEventStream.IsWaitingForActionCompletion(); - - if (isActiveFrame) - activityTimer.Reset(); - - bool isValidFrameTime = true; - if (activityTimer.ElapsedSeconds() > kMaxInactiveSeconds) + if (context->validate()) { - // Process a message or wait for a message. - // IsRenderingEnabled() can return false in case of rendering disabling and we must prevent - // possibility of infinity waiting in ProcessSingleMessage. - m_renderer.ProcessSingleMessage(m_renderer.IsRenderingEnabled()); - activityTimer.Reset(); + invalidContext = false; + ScreenBase modelView = m_renderer.ProcessEvents(modelViewChanged, viewportChanged); + if (viewportChanged) + m_renderer.OnResize(modelView); + + // Check for a frame is active. + bool isActiveFrame = modelViewChanged || viewportChanged; + + if (isActiveFrame) + m_renderer.PrepareScene(modelView); + + isActiveFrame |= m_renderer.m_myPositionController->IsWaitingForTimers(); + isActiveFrame |= m_renderer.m_texMng->UpdateDynamicTextures(); + m_renderer.m_routeRenderer->UpdatePreview(modelView); + + m_renderer.RenderScene(modelView); + + if (modelViewChanged || m_renderer.m_forceUpdateScene || m_renderer.m_forceUpdateUserMarks) + m_renderer.UpdateScene(modelView); + + isActiveFrame |= InterpolationHolder::Instance().Advance(frameTime); + AnimationSystem::Instance().Advance(frameTime); + + isActiveFrame |= m_renderer.m_userEventStream.IsWaitingForActionCompletion(); + + if (isActiveFrame) + activityTimer.Reset(); + + bool isValidFrameTime = true; + if (activityTimer.ElapsedSeconds() > kMaxInactiveSeconds) + { + // Process a message or wait for a message. + // IsRenderingEnabled() can return false in case of rendering disabling and we must prevent + // possibility of infinity waiting in ProcessSingleMessage. + m_renderer.ProcessSingleMessage(m_renderer.IsRenderingEnabled()); + activityTimer.Reset(); + timer.Reset(); + isValidFrameTime = false; + } + else + { + double availableTime = kVSyncInterval - timer.ElapsedSeconds(); + do + { + if (!m_renderer.ProcessSingleMessage(false /* waitForMessage */)) + break; + + activityTimer.Reset(); + availableTime = kVSyncInterval - timer.ElapsedSeconds(); + } + while (availableTime > 0.0); + } + + context->present(); + frameTime = timer.ElapsedSeconds(); timer.Reset(); - isValidFrameTime = false; + + // Limit fps in following mode. + double constexpr kFrameTime = 1.0 / 30.0; + if (isValidFrameTime && frameTime < kFrameTime && + m_renderer.m_myPositionController->IsRouteFollowingActive()) + { + auto const ms = static_cast((kFrameTime - frameTime) * 1000); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + } + + if (m_renderer.m_overlaysTracker->IsValid() && + showOverlaysEventsTimer.ElapsedSeconds() > kShowOverlaysEventsPeriod) + { + m_renderer.CollectShowOverlaysEvents(); + showOverlaysEventsTimer.Reset(); + } } else { - double availableTime = kVSyncInterval - timer.ElapsedSeconds(); - do + if (!invalidContext) { - if (!m_renderer.ProcessSingleMessage(false /* waitForMessage */)) - break; - - activityTimer.Reset(); - availableTime = kVSyncInterval - timer.ElapsedSeconds(); + LOG(LINFO, ("Invalid context. Rendering is stopped.")); + invalidContext = true; } - while (availableTime > 0.0); } - - context->present(); - frameTime = timer.ElapsedSeconds(); - timer.Reset(); - - // Limit fps in following mode. - double constexpr kFrameTime = 1.0 / 30.0; - if (isValidFrameTime && - m_renderer.m_myPositionController->IsRouteFollowingActive() && frameTime < kFrameTime) - { - uint32_t const ms = static_cast((kFrameTime - frameTime) * 1000); - std::this_thread::sleep_for(std::chrono::milliseconds(ms)); - } - - if (m_renderer.m_overlaysTracker->IsValid() && - showOverlaysEventsTimer.ElapsedSeconds() > kShowOverlaysEventsPeriod) - { - m_renderer.CollectShowOverlaysEvents(); - showOverlaysEventsTimer.Reset(); - } - m_renderer.CheckRenderingEnabled(); } diff --git a/iphone/Maps/Classes/EAGLView.mm b/iphone/Maps/Classes/EAGLView.mm index f19eb86ef2..5b6a9aee36 100644 --- a/iphone/Maps/Classes/EAGLView.mm +++ b/iphone/Maps/Classes/EAGLView.mm @@ -174,7 +174,7 @@ double getExactDPI(double contentScaleFactor) - (void)setPresentAvailable:(BOOL)available { - m_factory->CastFactory()->setPresentAvailable(available); + m_factory->setPresentAvailable(available); } - (MWMMapWidgets *)widgetsManager diff --git a/iphone/Maps/Classes/iosOGLContext.h b/iphone/Maps/Classes/iosOGLContext.h index 408e041dcf..79b857f1e0 100644 --- a/iphone/Maps/Classes/iosOGLContext.h +++ b/iphone/Maps/Classes/iosOGLContext.h @@ -15,12 +15,11 @@ public: iosOGLContext * contextToShareWith, bool needBuffers = false); ~iosOGLContext(); - virtual void makeCurrent(); - virtual void present(); - virtual void setDefaultFramebuffer(); - virtual void resize(int w, int h); - - void setPresentAvailable(bool available); + void makeCurrent() override; + void present() override; + void setDefaultFramebuffer() override; + void resize(int w, int h) override; + void setPresentAvailable(bool available) override; private: dp::ApiVersion m_apiVersion; diff --git a/iphone/Maps/Classes/iosOGLContextFactory.h b/iphone/Maps/Classes/iosOGLContextFactory.h index 88433a1624..8f906925cc 100644 --- a/iphone/Maps/Classes/iosOGLContextFactory.h +++ b/iphone/Maps/Classes/iosOGLContextFactory.h @@ -22,7 +22,7 @@ public: void waitForInitialization(dp::OGLContext * context) override; - void setPresentAvailable(bool available); + void setPresentAvailable(bool available) override; private: CAEAGLLayer * m_layer;