From 39235bedc64501f212d90102730a9d144c3c9b7d Mon Sep 17 00:00:00 2001 From: kgv Date: Mon, 12 Nov 2018 15:46:40 +0300 Subject: [PATCH] 0030364: Visualization, TKOpenGl - allow initializing a Surface-less EGL context 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 | 2 - src/OpenGl/OpenGl_GraphicDriver.cxx | 82 +++++++++++++++++++---------- src/OpenGl/OpenGl_Window.cxx | 51 ++++++++++++++++-- src/V3d/V3d_View.cxx | 11 ++-- src/V3d/V3d_Viewer.cxx | 6 ++- 5 files changed, 112 insertions(+), 40 deletions(-) diff --git a/src/OpenGl/OpenGl_Context.cxx b/src/OpenGl/OpenGl_Context.cxx index 125102fe75..52509f4c15 100644 --- a/src/OpenGl/OpenGl_Context.cxx +++ b/src/OpenGl/OpenGl_Context.cxx @@ -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!"); diff --git a/src/OpenGl/OpenGl_GraphicDriver.cxx b/src/OpenGl/OpenGl_GraphicDriver.cxx index 23cba938f5..ee2ea156c1 100644 --- a/src/OpenGl/OpenGl_GraphicDriver.cxx +++ b/src/OpenGl/OpenGl_GraphicDriver.cxx @@ -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 diff --git a/src/OpenGl/OpenGl_Window.cxx b/src/OpenGl/OpenGl_Window.cxx index 73891f2b25..c812775bca 100644 --- a/src/OpenGl/OpenGl_Window.cxx +++ b/src/OpenGl/OpenGl_Window.cxx @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -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); diff --git a/src/V3d/V3d_View.cxx b/src/V3d/V3d_View.cxx index 3f3edbbe47..547af8334d 100644 --- a/src/V3d/V3d_View.cxx +++ b/src/V3d/V3d_View.cxx @@ -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(); + } } //============================================================================= diff --git a/src/V3d/V3d_Viewer.cxx b/src/V3d/V3d_Viewer.cxx index 3e9e49221b..95f344f424 100644 --- a/src/V3d/V3d_Viewer.cxx +++ b/src/V3d/V3d_Viewer.cxx @@ -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); + } } // ======================================================================== -- 2.39.5