2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 #include <dali/internal/graphics/gles/egl-implementation.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/public-api/common/dali-vector.h>
28 #include <dali/public-api/dali-adaptor-common.h>
29 #include <dali/internal/graphics/gles/gl-implementation.h>
30 #include <dali/internal/graphics/gles/egl-debug.h>
32 // EGL constants use C style casts
33 #pragma GCC diagnostic push
34 #pragma GCC diagnostic ignored "-Wold-style-cast"
38 const uint32_t THRESHOLD_SWAPBUFFER_COUNT = 5;
39 const uint32_t CHECK_EXTENSION_NUMBER = 2;
40 const std::string EGL_KHR_SURFACELESS_CONTEXT = "EGL_KHR_surfaceless_context";
41 const std::string EGL_KHR_CREATE_CONTEXT = "EGL_KHR_create_context";
53 #define TEST_EGL_ERROR(lastCommand) \
55 EGLint err = eglGetError(); \
56 if (err != EGL_SUCCESS) \
58 DALI_LOG_ERROR("EGL error after %s\n", lastCommand); \
59 Egl::PrintError(err); \
60 DALI_ASSERT_ALWAYS(0 && "EGL error"); \
64 EglImplementation::EglImplementation( int multiSamplingLevel,
65 Integration::DepthBufferAvailable depthBufferRequired,
66 Integration::StencilBufferAvailable stencilBufferRequired ,
67 Integration::PartialUpdateAvailable partialUpdateRequired )
69 mEglNativeDisplay( 0 ),
70 mEglNativeWindow( 0 ),
71 mCurrentEglNativePixmap( 0 ),
75 mCurrentEglSurface( 0 ),
76 mCurrentEglContext( EGL_NO_CONTEXT ),
77 mMultiSamplingLevel( multiSamplingLevel ),
79 mColorDepth( COLOR_DEPTH_24 ),
80 mGlesInitialized( false ),
81 mIsOwnSurface( true ),
83 mDepthBufferRequired( depthBufferRequired == Integration::DepthBufferAvailable::TRUE ),
84 mStencilBufferRequired( stencilBufferRequired == Integration::StencilBufferAvailable::TRUE ),
85 mPartialUpdateRequired( partialUpdateRequired == Integration::PartialUpdateAvailable::TRUE ),
86 mIsSurfacelessContextSupported( false ),
87 mIsKhrCreateContextSupported( false ),
88 mSwapBufferCountAfterResume( 0 ),
89 mEglSetDamageRegionKHR( 0 ),
90 mEglSwapBuffersWithDamageKHR( 0 )
94 EglImplementation::~EglImplementation()
99 bool EglImplementation::InitializeGles( EGLNativeDisplayType display, bool isOwnSurface )
101 if ( !mGlesInitialized )
103 mEglNativeDisplay = display;
105 // Try to get the display connection for the native display first
106 mEglDisplay = eglGetDisplay( mEglNativeDisplay );
108 if( mEglDisplay == EGL_NO_DISPLAY )
110 // If failed, try to get the default display connection
111 mEglDisplay = eglGetDisplay( EGL_DEFAULT_DISPLAY );
114 if( mEglDisplay == EGL_NO_DISPLAY )
116 // Still failed to get a display connection
117 throw Dali::DaliException( "", "OpenGL ES is not supported");
120 EGLint majorVersion = 0;
121 EGLint minorVersion = 0;
122 if ( !eglInitialize( mEglDisplay, &majorVersion, &minorVersion ) )
126 eglBindAPI(EGL_OPENGL_ES_API);
128 mIsOwnSurface = isOwnSurface;
131 // Query EGL extensions to check whether surfaceless context is supported
132 const char* const extensionStr = eglQueryString( mEglDisplay, EGL_EXTENSIONS );
133 std::istringstream stream( extensionStr );
134 std::string currentExtension;
135 uint32_t extensionCheckCount = 0;
136 while( std::getline( stream, currentExtension, ' ' ) && extensionCheckCount < CHECK_EXTENSION_NUMBER )
138 if( currentExtension == EGL_KHR_SURFACELESS_CONTEXT )
140 mIsSurfacelessContextSupported = true;
141 extensionCheckCount++;
143 if( currentExtension == EGL_KHR_CREATE_CONTEXT )
145 mIsKhrCreateContextSupported = true;
146 extensionCheckCount++;
150 mGlesInitialized = true;
152 // We want to display this information all the time, so use the LogMessage directly
153 Integration::Log::LogMessage(Integration::Log::DebugInfo, "EGL Information\n"
154 " PartialUpdate %d\n"
159 mPartialUpdateRequired,
160 eglQueryString( mEglDisplay, EGL_VENDOR ),
161 eglQueryString( mEglDisplay, EGL_VERSION ),
162 eglQueryString( mEglDisplay, EGL_CLIENT_APIS ),
165 return mGlesInitialized;
168 bool EglImplementation::CreateContext()
170 // make sure a context isn't created twice
171 DALI_ASSERT_ALWAYS( (mEglContext == 0) && "EGL context recreated" );
173 mEglContext = eglCreateContext(mEglDisplay, mEglConfig, NULL, &(mContextAttribs[0]));
174 TEST_EGL_ERROR("eglCreateContext render thread");
176 DALI_ASSERT_ALWAYS( EGL_NO_CONTEXT != mEglContext && "EGL context not created" );
178 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VENDOR : %s ***\n", glGetString(GL_VENDOR));
179 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_RENDERER : %s ***\n", glGetString(GL_RENDERER));
180 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VERSION : %s ***\n", glGetString(GL_VERSION));
181 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_SHADING_LANGUAGE_VERSION : %s***\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
182 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** Supported Extensions ***\n%s\n\n", glGetString(GL_EXTENSIONS));
184 mEglSetDamageRegionKHR = reinterpret_cast<PFNEGLSETDAMAGEREGIONKHRPROC>(eglGetProcAddress("eglSetDamageRegionKHR"));
185 if (!mEglSetDamageRegionKHR)
187 DALI_LOG_ERROR("Coudn't find eglSetDamageRegionKHR!\n");
188 mPartialUpdateRequired = false;
190 mEglSwapBuffersWithDamageKHR = reinterpret_cast<PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC>(eglGetProcAddress("eglSwapBuffersWithDamageKHR"));
191 if (!mEglSwapBuffersWithDamageKHR)
193 DALI_LOG_ERROR("Coudn't find eglSwapBuffersWithDamageKHR!\n");
194 mPartialUpdateRequired = false;
199 bool EglImplementation::CreateWindowContext( EGLContext& eglContext )
201 // make sure a context isn't created twice
202 DALI_ASSERT_ALWAYS( (eglContext == 0) && "EGL context recreated" );
204 eglContext = eglCreateContext(mEglDisplay, mEglConfig, mEglContext, &(mContextAttribs[0]));
205 TEST_EGL_ERROR("eglCreateContext render thread");
207 DALI_ASSERT_ALWAYS( EGL_NO_CONTEXT != eglContext && "EGL context not created" );
209 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VENDOR : %s ***\n", glGetString(GL_VENDOR));
210 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_RENDERER : %s ***\n", glGetString(GL_RENDERER));
211 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VERSION : %s ***\n", glGetString(GL_VERSION));
212 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_SHADING_LANGUAGE_VERSION : %s***\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
213 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** Supported Extensions ***\n%s\n\n", glGetString(GL_EXTENSIONS));
215 mEglWindowContexts.push_back( eglContext );
217 mEglSetDamageRegionKHR = reinterpret_cast<PFNEGLSETDAMAGEREGIONKHRPROC>(eglGetProcAddress("eglSetDamageRegionKHR"));
218 if (!mEglSetDamageRegionKHR)
220 DALI_LOG_ERROR("Coudn't find eglSetDamageRegionKHR!\n");
221 mPartialUpdateRequired = false;
223 mEglSwapBuffersWithDamageKHR = reinterpret_cast<PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC>(eglGetProcAddress("eglSwapBuffersWithDamageKHR"));
224 if (!mEglSwapBuffersWithDamageKHR)
226 DALI_LOG_ERROR("Coudn't find eglSwapBuffersWithDamageKHR!\n");
227 mPartialUpdateRequired = false;
232 void EglImplementation::DestroyContext( EGLContext& eglContext )
236 eglDestroyContext( mEglDisplay, eglContext );
241 void EglImplementation::DestroySurface( EGLSurface& eglSurface )
243 if(mIsOwnSurface && eglSurface)
245 // Make context null to prevent crash in driver side
247 eglDestroySurface( mEglDisplay, eglSurface );
252 void EglImplementation::MakeContextCurrent( EGLSurface eglSurface, EGLContext eglContext )
254 if (mCurrentEglContext == eglContext)
259 mCurrentEglSurface = eglSurface;
263 eglMakeCurrent( mEglDisplay, eglSurface, eglSurface, eglContext );
265 mCurrentEglContext = eglContext;
268 EGLint error = eglGetError();
270 if ( error != EGL_SUCCESS )
272 Egl::PrintError(error);
274 DALI_ASSERT_ALWAYS(false && "MakeContextCurrent failed!");
278 void EglImplementation::MakeCurrent( EGLNativePixmapType pixmap, EGLSurface eglSurface )
280 if (mCurrentEglContext == mEglContext)
285 mCurrentEglNativePixmap = pixmap;
286 mCurrentEglSurface = eglSurface;
290 eglMakeCurrent( mEglDisplay, eglSurface, eglSurface, mEglContext );
292 mCurrentEglContext = mEglContext;
295 EGLint error = eglGetError();
297 if ( error != EGL_SUCCESS )
299 Egl::PrintError(error);
301 DALI_ASSERT_ALWAYS(false && "MakeCurrent failed!");
305 void EglImplementation::MakeContextNull()
307 // clear the current context
308 eglMakeCurrent( mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
309 mCurrentEglContext = EGL_NO_CONTEXT;
312 void EglImplementation::TerminateGles()
314 if ( mGlesInitialized )
316 // Make context null to prevent crash in driver side
319 for ( auto eglSurface : mEglWindowSurfaces )
321 if(mIsOwnSurface && eglSurface)
323 eglDestroySurface(mEglDisplay, eglSurface);
326 eglDestroyContext(mEglDisplay, mEglContext);
327 for ( auto eglContext : mEglWindowContexts )
329 eglDestroyContext(mEglDisplay, eglContext);
332 eglTerminate(mEglDisplay);
337 mCurrentEglSurface = NULL;
338 mCurrentEglContext = EGL_NO_CONTEXT;
340 mGlesInitialized = false;
344 bool EglImplementation::IsGlesInitialized() const
346 return mGlesInitialized;
349 void EglImplementation::SwapBuffers( EGLSurface& eglSurface )
351 if ( eglSurface != EGL_NO_SURFACE ) // skip if using surfaceless context
353 #ifndef DALI_PROFILE_UBUNTU
354 if( mSwapBufferCountAfterResume < THRESHOLD_SWAPBUFFER_COUNT )
356 DALI_LOG_RELEASE_INFO( "EglImplementation::SwapBuffers started.\n" );
358 #endif //DALI_PROFILE_UBUNTU
360 // DALI_LOG_ERROR("EglImplementation::SwapBuffers()\n");
361 eglSwapBuffers( mEglDisplay, eglSurface );
363 #ifndef DALI_PROFILE_UBUNTU
364 if( mSwapBufferCountAfterResume < THRESHOLD_SWAPBUFFER_COUNT )
366 DALI_LOG_RELEASE_INFO( "EglImplementation::SwapBuffers finished.\n" );
367 mSwapBufferCountAfterResume++;
369 #endif //DALI_PROFILE_UBUNTU
373 EGLint EglImplementation::GetBufferAge(EGLSurface& eglSurface) const
376 eglQuerySurface(mEglDisplay, eglSurface, EGL_BUFFER_AGE_EXT, &age);
379 DALI_LOG_ERROR("eglQuerySurface(%d)\n", eglGetError());
383 // 0 - invalid buffer
387 DALI_LOG_ERROR("EglImplementation::GetBufferAge() buffer age %d > 3\n", age);
388 age = 0; // shoudn't be more than 3 back buffers, if there is just reset, I don't want to add extra history level
394 void EglImplementation::SetDamageRegion( EGLSurface& eglSurface, std::vector< Rect< int > >& damagedRects )
396 if( !mPartialUpdateRequired )
401 if( eglSurface != EGL_NO_SURFACE ) // skip if using surfaceless context
403 EGLBoolean result = mEglSetDamageRegionKHR( mEglDisplay, eglSurface, reinterpret_cast< int* >( damagedRects.data() ), 1 );
404 if (result == EGL_FALSE)
406 DALI_LOG_ERROR( "eglSetDamageRegionKHR(%d)\n", eglGetError() );
411 void EglImplementation::SwapBuffers(EGLSurface& eglSurface, const std::vector<Rect<int>>& damagedRects)
413 if (eglSurface != EGL_NO_SURFACE ) // skip if using surfaceless context
415 if (!mPartialUpdateRequired )
417 SwapBuffers(eglSurface);
421 #ifndef DALI_PROFILE_UBUNTU
422 if( mSwapBufferCountAfterResume < THRESHOLD_SWAPBUFFER_COUNT )
424 DALI_LOG_RELEASE_INFO( "EglImplementation::SwapBuffers started.\n" );
426 #endif //DALI_PROFILE_UBUNTU
428 EGLBoolean result = mEglSwapBuffersWithDamageKHR(mEglDisplay, eglSurface, reinterpret_cast<int*>(const_cast< std::vector< Rect< int > >& >( damagedRects ).data()), damagedRects.size());
429 if (result == EGL_FALSE)
431 DALI_LOG_ERROR("eglSwapBuffersWithDamageKHR(%d)\n", eglGetError());
434 #ifndef DALI_PROFILE_UBUNTU
435 if( mSwapBufferCountAfterResume < THRESHOLD_SWAPBUFFER_COUNT )
437 DALI_LOG_RELEASE_INFO( "EglImplementation::SwapBuffers finished.\n" );
438 mSwapBufferCountAfterResume++;
440 #endif //DALI_PROFILE_UBUNTU
444 void EglImplementation::CopyBuffers( EGLSurface& eglSurface )
446 eglCopyBuffers( mEglDisplay, eglSurface, mCurrentEglNativePixmap );
449 void EglImplementation::WaitGL()
454 bool EglImplementation::ChooseConfig( bool isWindowType, ColorDepth depth )
456 if(mEglConfig && isWindowType == mIsWindow && mColorDepth == depth)
462 mIsWindow = isWindowType;
465 Vector<EGLint> configAttribs;
466 configAttribs.Reserve(31);
470 configAttribs.PushBack( EGL_SURFACE_TYPE );
471 configAttribs.PushBack( EGL_WINDOW_BIT );
475 configAttribs.PushBack( EGL_SURFACE_TYPE );
476 configAttribs.PushBack( EGL_PIXMAP_BIT );
479 configAttribs.PushBack( EGL_RENDERABLE_TYPE );
481 if( mGlesVersion >= 30 )
483 configAttribs.PushBack( EGL_OPENGL_ES3_BIT_KHR );
487 configAttribs.PushBack( EGL_OPENGL_ES2_BIT );
490 // TODO: enable this flag when it becomes supported
491 // configAttribs.PushBack( EGL_CONTEXT_FLAGS_KHR );
492 // configAttribs.PushBack( EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR );
494 configAttribs.PushBack( EGL_RED_SIZE );
495 configAttribs.PushBack( 8 );
496 configAttribs.PushBack( EGL_GREEN_SIZE );
497 configAttribs.PushBack( 8 );
498 configAttribs.PushBack( EGL_BLUE_SIZE );
499 configAttribs.PushBack( 8 );
501 // For underlay video playback, we also need to set the alpha value of the 24/32bit window.
502 configAttribs.PushBack( EGL_ALPHA_SIZE );
503 configAttribs.PushBack( 8 );
505 configAttribs.PushBack( EGL_DEPTH_SIZE );
506 configAttribs.PushBack( mDepthBufferRequired ? 24 : 0 );
507 configAttribs.PushBack( EGL_STENCIL_SIZE );
508 configAttribs.PushBack( mStencilBufferRequired ? 8 : 0 );
510 #ifndef DALI_PROFILE_UBUNTU
511 if( mMultiSamplingLevel != EGL_DONT_CARE )
513 configAttribs.PushBack( EGL_SAMPLES );
514 configAttribs.PushBack( mMultiSamplingLevel );
515 configAttribs.PushBack( EGL_SAMPLE_BUFFERS );
516 configAttribs.PushBack( 1 );
518 #endif // DALI_PROFILE_UBUNTU
519 configAttribs.PushBack( EGL_NONE );
521 // Ensure number of configs is set to 1 as on some drivers,
522 // eglChooseConfig succeeds but does not actually create a proper configuration.
523 if ( ( eglChooseConfig( mEglDisplay, &(configAttribs[0]), &mEglConfig, 1, &numConfigs ) != EGL_TRUE ) ||
524 ( numConfigs != 1 ) )
526 if( mGlesVersion >= 30 )
529 DALI_LOG_ERROR("Fail to use OpenGL es 3.0. Retrying to use OpenGL es 2.0.");
533 if ( numConfigs != 1 )
535 DALI_LOG_ERROR("No configurations found.\n");
537 TEST_EGL_ERROR("eglChooseConfig");
540 EGLint error = eglGetError();
543 case EGL_BAD_DISPLAY:
545 DALI_LOG_ERROR("Display is not an EGL display connection\n");
548 case EGL_BAD_ATTRIBUTE:
550 DALI_LOG_ERROR("The parameter configAttribs contains an invalid frame buffer configuration attribute or an attribute value that is unrecognized or out of range\n");
553 case EGL_NOT_INITIALIZED:
555 DALI_LOG_ERROR("Display has not been initialized\n");
558 case EGL_BAD_PARAMETER:
560 DALI_LOG_ERROR("The parameter numConfig is NULL\n");
565 DALI_LOG_ERROR("Unknown error.\n");
568 DALI_ASSERT_ALWAYS(false && "eglChooseConfig failed!");
571 Integration::Log::LogMessage(Integration::Log::DebugInfo, "Using OpenGL es %d.%d.\n", mGlesVersion / 10, mGlesVersion % 10 );
573 mContextAttribs.Clear();
574 if( mIsKhrCreateContextSupported )
576 mContextAttribs.Reserve(5);
577 mContextAttribs.PushBack( EGL_CONTEXT_MAJOR_VERSION_KHR );
578 mContextAttribs.PushBack( mGlesVersion / 10 );
579 mContextAttribs.PushBack( EGL_CONTEXT_MINOR_VERSION_KHR );
580 mContextAttribs.PushBack( mGlesVersion % 10 );
584 mContextAttribs.Reserve(3);
585 mContextAttribs.PushBack( EGL_CONTEXT_CLIENT_VERSION );
586 mContextAttribs.PushBack( mGlesVersion / 10 );
588 mContextAttribs.PushBack( EGL_NONE );
593 EGLSurface EglImplementation::CreateSurfaceWindow( EGLNativeWindowType window, ColorDepth depth )
595 mEglNativeWindow = window;
600 ChooseConfig(mIsWindow, mColorDepth);
602 mCurrentEglSurface = eglCreateWindowSurface( mEglDisplay, mEglConfig, mEglNativeWindow, NULL );
603 TEST_EGL_ERROR("eglCreateWindowSurface");
605 DALI_ASSERT_ALWAYS( mCurrentEglSurface && "Create window surface failed" );
607 return mCurrentEglSurface;
610 EGLSurface EglImplementation::CreateSurfacePixmap( EGLNativePixmapType pixmap, ColorDepth depth )
612 mCurrentEglNativePixmap = pixmap;
617 ChooseConfig(mIsWindow, mColorDepth);
619 mCurrentEglSurface = eglCreatePixmapSurface( mEglDisplay, mEglConfig, mCurrentEglNativePixmap, NULL );
620 TEST_EGL_ERROR("eglCreatePixmapSurface");
622 DALI_ASSERT_ALWAYS( mCurrentEglSurface && "Create pixmap surface failed" );
624 return mCurrentEglSurface;
627 bool EglImplementation::ReplaceSurfaceWindow( EGLNativeWindowType window, EGLSurface& eglSurface, EGLContext& eglContext )
629 bool contextLost = false;
631 // display connection has not changed, then we can just create a new surface
632 // the surface is bound to the context, so set the context to null
637 // destroy the surface
638 DestroySurface( eglSurface );
641 // create the EGL surface
642 EGLSurface newEglSurface = CreateSurfaceWindow( window, mColorDepth );
644 // set the context to be current with the new surface
645 MakeContextCurrent( newEglSurface, eglContext );
650 bool EglImplementation::ReplaceSurfacePixmap( EGLNativePixmapType pixmap, EGLSurface& eglSurface )
652 bool contextLost = false;
654 // display connection has not changed, then we can just create a new surface
655 // create the EGL surface
656 eglSurface = CreateSurfacePixmap( pixmap, mColorDepth );
658 // set the eglSurface to be current
659 MakeCurrent( pixmap, eglSurface );
664 void EglImplementation::SetGlesVersion( const int32_t glesVersion )
666 mGlesVersion = glesVersion;
669 void EglImplementation::SetFirstFrameAfterResume()
671 mSwapBufferCountAfterResume = 0;
674 EGLDisplay EglImplementation::GetDisplay() const
679 EGLContext EglImplementation::GetContext() const
684 int32_t EglImplementation::GetGlesVersion() const
689 bool EglImplementation::IsSurfacelessContextSupported() const
691 return mIsSurfacelessContextSupported;
694 void EglImplementation::WaitClient()
696 // Wait for EGL to finish executing all rendering calls for the current context
697 if ( eglWaitClient() != EGL_TRUE )
699 TEST_EGL_ERROR("eglWaitClient");
703 bool EglImplementation::IsPartialUpdateRequired() const
705 return mPartialUpdateRequired;
708 } // namespace Adaptor
710 } // namespace Internal
714 #pragma GCC diagnostic pop