0030364: Visualization, TKOpenGl - allow initializing a Surface-less EGL context
authorkgv <kgv@opencascade.com>
Mon, 12 Nov 2018 12:46:40 +0000 (15:46 +0300)
committersan <san@opencascade.com>
Sun, 23 Dec 2018 17:25:14 +0000 (20:25 +0300)
OpenGl_Context::MakeCurrent()/OpenGl_Context::IsCurrent() have been modified to NOT fail
in case if myWindow is EGL_NO_SURFACE (valid off-screen rendering case within EGL).
OpenGl_GraphicDriver::InitEglContext() now finds EGL surface config in case if it has not been passed by argument.

OpenGl_Window constructor now allows wrapping an off-screen rendering surface EGL_NO_SURFACE.
However, it still creates a dummy surface eglCreatePbufferSurface() to workaround bugs in some GLES drivers (Vivante GC2000).

V3d_View::SetWindow()/V3d_View::MustBeResized()/V3d_Viewer::SetViewOn() have been modified
to avoid implicit View redraw (leading to undefined behavior/crashes in case if rendering
should be done into default FBO defined right after V3d_View initialization).

src/OpenGl/OpenGl_Context.cxx
src/OpenGl/OpenGl_GraphicDriver.cxx
src/OpenGl/OpenGl_Window.cxx
src/V3d/V3d_View.cxx
src/V3d/V3d_Viewer.cxx

index 125102f..52509f4 100644 (file)
@@ -544,7 +544,6 @@ Standard_Boolean OpenGl_Context::IsCurrent() const
 {
 #if defined(HAVE_EGL)
   if ((EGLDisplay )myDisplay  == EGL_NO_DISPLAY
-   || (EGLSurface )myWindow   == EGL_NO_SURFACE
    || (EGLContext )myGContext == EGL_NO_CONTEXT)
   {
     return Standard_False;
@@ -580,7 +579,6 @@ Standard_Boolean OpenGl_Context::MakeCurrent()
 {
 #if defined(HAVE_EGL)
   if ((EGLDisplay )myDisplay  == EGL_NO_DISPLAY
-   || (EGLSurface )myWindow   == EGL_NO_SURFACE
    || (EGLContext )myGContext == EGL_NO_CONTEXT)
   {
     Standard_ProgramError_Raise_if (myIsInitialized, "OpenGl_Context::Init() should be called before!");
index 23cba93..ee2ea15 100644 (file)
@@ -55,6 +55,45 @@ IMPLEMENT_STANDARD_RTTIEXT(OpenGl_GraphicDriver,Graphic3d_GraphicDriver)
 namespace
 {
   static const Handle(OpenGl_Context) TheNullGlCtx;
+
+#if defined(HAVE_EGL) || defined(HAVE_GLES2) || defined(OCCT_UWP) || defined(__ANDROID__) || defined(__QNX__)
+  //! Wrapper over eglChooseConfig() called with preferred defaults.
+  static EGLConfig chooseEglSurfConfig (EGLDisplay theDisplay)
+  {
+    EGLint aConfigAttribs[] =
+    {
+      EGL_RED_SIZE,     8,
+      EGL_GREEN_SIZE,   8,
+      EGL_BLUE_SIZE,    8,
+      EGL_ALPHA_SIZE,   0,
+      EGL_DEPTH_SIZE,   24,
+      EGL_STENCIL_SIZE, 8,
+    #if defined(GL_ES_VERSION_2_0)
+      EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+    #else
+      EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
+    #endif
+      EGL_NONE
+    };
+
+    EGLConfig aCfg = NULL;
+    EGLint aNbConfigs = 0;
+    if (eglChooseConfig (theDisplay, aConfigAttribs, &aCfg, 1, &aNbConfigs) == EGL_TRUE
+     || aCfg != NULL)
+    {
+      return aCfg;
+    }
+
+    eglGetError();
+    aConfigAttribs[4 * 2 + 1] = 16; // try config with smaller depth buffer
+    if (eglChooseConfig (theDisplay, aConfigAttribs, &aCfg, 1, &aNbConfigs) != EGL_TRUE
+     || aCfg == NULL)
+    {
+      eglGetError();
+    }
+    return aCfg;
+  }
+#endif
 }
 
 // =======================================================================
@@ -299,34 +338,11 @@ Standard_Boolean OpenGl_GraphicDriver::InitContext()
     return Standard_False;
   }
 
-  EGLint aConfigAttribs[] =
-  {
-    EGL_RED_SIZE,     8,
-    EGL_GREEN_SIZE,   8,
-    EGL_BLUE_SIZE,    8,
-    EGL_ALPHA_SIZE,   0,
-    EGL_DEPTH_SIZE,   24,
-    EGL_STENCIL_SIZE, 8,
-  #if defined(GL_ES_VERSION_2_0)
-    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
-  #else
-    EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
-  #endif
-    EGL_NONE
-  };
-
-  EGLint aNbConfigs = 0;
-  if (eglChooseConfig ((EGLDisplay )myEglDisplay, aConfigAttribs, &myEglConfig, 1, &aNbConfigs) != EGL_TRUE
-   || myEglConfig == NULL)
+  myEglConfig = chooseEglSurfConfig ((EGLDisplay )myEglDisplay);
+  if (myEglConfig == NULL)
   {
-    eglGetError();
-    aConfigAttribs[4 * 2 + 1] = 16; // try config with smaller depth buffer
-    if (eglChooseConfig ((EGLDisplay )myEglDisplay, aConfigAttribs, &myEglConfig, 1, &aNbConfigs) != EGL_TRUE
-     || myEglConfig == NULL)
-    {
-      ::Message::DefaultMessenger()->Send ("Error: EGL does not provide compatible configurations!", Message_Fail);
-      return Standard_False;
-    }
+    ::Message::DefaultMessenger()->Send ("Error: EGL does not provide compatible configurations!", Message_Fail);
+    return Standard_False;
   }
 
 #if defined(GL_ES_VERSION_2_0)
@@ -384,14 +400,22 @@ Standard_Boolean OpenGl_GraphicDriver::InitEglContext (Aspect_Display          t
 #endif
 
   if ((EGLDisplay )theEglDisplay == EGL_NO_DISPLAY
-   || (EGLContext )theEglContext == EGL_NO_CONTEXT
-   || theEglConfig == NULL)
+   || (EGLContext )theEglContext == EGL_NO_CONTEXT)
   {
     return Standard_False;
   }
   myEglDisplay = theEglDisplay;
   myEglContext = theEglContext;
   myEglConfig  = theEglConfig;
+  if (theEglConfig == NULL)
+  {
+    myEglConfig = chooseEglSurfConfig ((EGLDisplay )myEglDisplay);
+    if (myEglConfig == NULL)
+    {
+      ::Message::DefaultMessenger()->Send ("Error: EGL does not provide compatible configurations!", Message_Fail);
+      return Standard_False;
+    }
+  }
   return Standard_True;
 }
 #endif
index 73891f2..c812775 100644 (file)
@@ -18,6 +18,7 @@
 #include <OpenGl_Context.hxx>
 #include <OpenGl_GraphicDriver.hxx>
 #include <OpenGl_Window.hxx>
+#include <OpenGl_FrameBuffer.hxx>
 
 #include <Aspect_GraphicDeviceDefinitionError.hxx>
 #include <Graphic3d_TransformUtils.hxx>
@@ -181,14 +182,15 @@ OpenGl_Window::OpenGl_Window (const Handle(OpenGl_GraphicDriver)& theDriver,
   EGLConfig  anEglConfig  = (EGLConfig  )theDriver->getRawGlConfig();
   if (anEglDisplay == EGL_NO_DISPLAY
    || anEglContext == EGL_NO_CONTEXT
-   || anEglConfig == NULL)
+   || (anEglConfig == NULL
+    && (EGLContext )theGContext == EGL_NO_CONTEXT))
   {
     throw Aspect_GraphicDeviceDefinitionError("OpenGl_Window, EGL does not provide compatible configurations!");
     return;
   }
 
   EGLSurface anEglSurf = EGL_NO_SURFACE;
-  if (theGContext == (EGLContext )EGL_NO_CONTEXT)
+  if ((EGLContext )theGContext == EGL_NO_CONTEXT)
   {
     // create new surface
     anEglSurf = eglCreateWindowSurface (anEglDisplay,
@@ -211,8 +213,24 @@ OpenGl_Window::OpenGl_Window (const Handle(OpenGl_GraphicDriver)& theDriver,
     anEglSurf = eglGetCurrentSurface(EGL_DRAW);
     if (anEglSurf == EGL_NO_SURFACE)
     {
-      throw Aspect_GraphicDeviceDefinitionError("OpenGl_Window, EGL is unable to retrieve current surface!");
-      return;
+      // window-less EGL context (off-screen)
+      //throw Aspect_GraphicDeviceDefinitionError("OpenGl_Window, EGL is unable to retrieve current surface!");
+      if (anEglConfig != NULL)
+      {
+        const int aSurfAttribs[] =
+        {
+          EGL_WIDTH,  myWidth,
+          EGL_HEIGHT, myHeight,
+          EGL_NONE
+        };
+        anEglSurf = eglCreatePbufferSurface (anEglDisplay, anEglConfig, aSurfAttribs);
+        if (anEglSurf == EGL_NO_SURFACE)
+        {
+          throw Aspect_GraphicDeviceDefinitionError("OpenGl_Window, EGL is unable to create off-screen surface!");
+        }
+      }
+      myGlContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_LOW,
+                                "OpenGl_Window::CreateWindow: WARNING, a Window is created without a EGL Surface!");
     }
   }
 
@@ -715,7 +733,30 @@ void OpenGl_Window::Init()
     return;
 
 #if defined(HAVE_EGL)
-  if (!myPlatformWindow->IsVirtual())
+  if ((EGLSurface )myGlContext->myWindow == EGL_NO_SURFACE)
+  {
+    // define an offscreen default FBO to avoid rendering into EGL_NO_SURFACE;
+    // note that this code is currently never called, since eglCreatePbufferSurface() is used instead as more robust solution
+    // for offscreen rendering on bugged OpenGL ES drivers
+    Handle(OpenGl_FrameBuffer) aDefFbo = myGlContext->SetDefaultFrameBuffer (Handle(OpenGl_FrameBuffer)());
+    if (!aDefFbo.IsNull())
+    {
+      aDefFbo->Release (myGlContext.operator->());
+    }
+    else
+    {
+      aDefFbo = new OpenGl_FrameBuffer();
+    }
+
+    if (!aDefFbo->InitWithRB (myGlContext, myWidth, myHeight, GL_RGBA8, GL_DEPTH24_STENCIL8))
+    {
+      TCollection_AsciiString aMsg ("OpenGl_Window::CreateWindow: default FBO creation failed");
+      throw Aspect_GraphicDeviceDefinitionError(aMsg.ToCString());
+    }
+    myGlContext->SetDefaultFrameBuffer (aDefFbo);
+    aDefFbo->BindBuffer (myGlContext);
+  }
+  else if (!myPlatformWindow->IsVirtual())
   {
     eglQuerySurface ((EGLDisplay )myGlContext->myDisplay, (EGLSurface )myGlContext->myWindow, EGL_WIDTH,  &myWidth);
     eglQuerySurface ((EGLDisplay )myGlContext->myDisplay, (EGLSurface )myGlContext->myWindow, EGL_HEIGHT, &myHeight);
index 3f3edbb..547af83 100644 (file)
@@ -189,7 +189,10 @@ void V3d_View::SetWindow (const Handle(Aspect_Window)&  theWindow,
   myView->SetWindow (theWindow, theContext);
   MyViewer->SetViewOn (this);
   SetRatio();
-  Redraw();
+  if (myImmediateUpdate)
+  {
+    Redraw();
+  }
 }
 
 //=============================================================================
@@ -412,8 +415,10 @@ void V3d_View::MustBeResized()
   myView->Resized();
 
   SetRatio();
-
-  Redraw();
+  if (myImmediateUpdate)
+  {
+    Redraw();
+  }
 }
 
 //=============================================================================
index 3e9e492..95f344f 100644 (file)
@@ -152,7 +152,11 @@ void V3d_Viewer::SetViewOn (const Handle(V3d_View)& theView)
 
   theView->SetGrid (myPrivilegedPlane, Grid ());
   theView->SetGridActivity (Grid ()->IsActive ());
-  theView->Redraw();
+  if (theView->SetImmediateUpdate (Standard_False))
+  {
+    theView->Redraw();
+    theView->SetImmediateUpdate (Standard_True);
+  }
 }
 
 // ========================================================================