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 = 4;
40 const std::string EGL_KHR_SURFACELESS_CONTEXT = "EGL_KHR_surfaceless_context";
41 const std::string EGL_KHR_CREATE_CONTEXT = "EGL_KHR_create_context";
42 const std::string EGL_KHR_PARTIAL_UPDATE = "EGL_KHR_partial_update";
43 const std::string EGL_KHR_SWAP_BUFFERS_WITH_DAMAGE = "EGL_KHR_swap_buffers_with_damage";
56 #define TEST_EGL_ERROR(lastCommand) \
58 EGLint err = eglGetError(); \
59 if (err != EGL_SUCCESS) \
61 DALI_LOG_ERROR("EGL error after %s\n", lastCommand); \
62 Egl::PrintError(err); \
63 DALI_ASSERT_ALWAYS(0 && "EGL error"); \
67 EglImplementation::EglImplementation( int multiSamplingLevel,
68 Integration::DepthBufferAvailable depthBufferRequired,
69 Integration::StencilBufferAvailable stencilBufferRequired ,
70 Integration::PartialUpdateAvailable partialUpdateRequired )
72 mEglNativeDisplay( 0 ),
73 mEglNativeWindow( 0 ),
74 mCurrentEglNativePixmap( 0 ),
78 mCurrentEglSurface( 0 ),
79 mCurrentEglContext( EGL_NO_CONTEXT ),
80 mMultiSamplingLevel( multiSamplingLevel ),
82 mColorDepth( COLOR_DEPTH_24 ),
83 mGlesInitialized( false ),
84 mIsOwnSurface( true ),
86 mDepthBufferRequired( depthBufferRequired == Integration::DepthBufferAvailable::TRUE ),
87 mStencilBufferRequired( stencilBufferRequired == Integration::StencilBufferAvailable::TRUE ),
88 mPartialUpdateRequired( partialUpdateRequired == Integration::PartialUpdateAvailable::TRUE ),
89 mIsSurfacelessContextSupported( false ),
90 mIsKhrCreateContextSupported( false ),
91 mSwapBufferCountAfterResume( 0 ),
92 mEglSetDamageRegionKHR( 0 ),
93 mEglSwapBuffersWithDamageKHR( 0 )
97 EglImplementation::~EglImplementation()
102 bool EglImplementation::InitializeGles( EGLNativeDisplayType display, bool isOwnSurface )
104 if ( !mGlesInitialized )
106 mEglNativeDisplay = display;
108 // Try to get the display connection for the native display first
109 mEglDisplay = eglGetDisplay( mEglNativeDisplay );
111 if( mEglDisplay == EGL_NO_DISPLAY )
113 // If failed, try to get the default display connection
114 mEglDisplay = eglGetDisplay( EGL_DEFAULT_DISPLAY );
117 if( mEglDisplay == EGL_NO_DISPLAY )
119 // Still failed to get a display connection
120 throw Dali::DaliException( "", "OpenGL ES is not supported");
123 EGLint majorVersion = 0;
124 EGLint minorVersion = 0;
125 if ( !eglInitialize( mEglDisplay, &majorVersion, &minorVersion ) )
129 eglBindAPI(EGL_OPENGL_ES_API);
131 mIsOwnSurface = isOwnSurface;
134 // Query EGL extensions to check whether surfaceless context is supported
135 const char* const extensionStr = eglQueryString( mEglDisplay, EGL_EXTENSIONS );
136 std::istringstream stream( extensionStr );
137 std::string currentExtension;
138 uint32_t extensionCheckCount = 0;
139 bool isKhrPartialUpdateSupported = false;
140 bool isKhrSwapBuffersWithDamageSupported = false;
141 while( std::getline( stream, currentExtension, ' ' ) && extensionCheckCount < CHECK_EXTENSION_NUMBER )
143 if( currentExtension == EGL_KHR_SURFACELESS_CONTEXT )
145 mIsSurfacelessContextSupported = true;
146 extensionCheckCount++;
148 if( currentExtension == EGL_KHR_CREATE_CONTEXT )
150 mIsKhrCreateContextSupported = true;
151 extensionCheckCount++;
153 if( currentExtension == EGL_KHR_PARTIAL_UPDATE )
155 isKhrPartialUpdateSupported = true;
156 extensionCheckCount++;
158 if( currentExtension == EGL_KHR_SWAP_BUFFERS_WITH_DAMAGE )
160 isKhrSwapBuffersWithDamageSupported = true;
161 extensionCheckCount++;
165 if( !isKhrPartialUpdateSupported || !isKhrSwapBuffersWithDamageSupported )
167 mPartialUpdateRequired = false;
170 mGlesInitialized = true;
172 // We want to display this information all the time, so use the LogMessage directly
173 Integration::Log::LogMessage(Integration::Log::DebugInfo, "EGL Information\n"
174 " PartialUpdate %d\n"
179 mPartialUpdateRequired,
180 eglQueryString( mEglDisplay, EGL_VENDOR ),
181 eglQueryString( mEglDisplay, EGL_VERSION ),
182 eglQueryString( mEglDisplay, EGL_CLIENT_APIS ),
185 return mGlesInitialized;
188 bool EglImplementation::CreateContext()
190 // make sure a context isn't created twice
191 DALI_ASSERT_ALWAYS( (mEglContext == 0) && "EGL context recreated" );
193 mEglContext = eglCreateContext(mEglDisplay, mEglConfig, NULL, &(mContextAttribs[0]));
194 TEST_EGL_ERROR("eglCreateContext render thread");
196 DALI_ASSERT_ALWAYS( EGL_NO_CONTEXT != mEglContext && "EGL context not created" );
198 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VENDOR : %s ***\n", glGetString(GL_VENDOR));
199 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_RENDERER : %s ***\n", glGetString(GL_RENDERER));
200 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VERSION : %s ***\n", glGetString(GL_VERSION));
201 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_SHADING_LANGUAGE_VERSION : %s***\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
202 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** Supported Extensions ***\n%s\n\n", glGetString(GL_EXTENSIONS));
204 mEglSetDamageRegionKHR = reinterpret_cast<PFNEGLSETDAMAGEREGIONKHRPROC>(eglGetProcAddress("eglSetDamageRegionKHR"));
205 if (!mEglSetDamageRegionKHR)
207 DALI_LOG_ERROR("Coudn't find eglSetDamageRegionKHR!\n");
208 mPartialUpdateRequired = false;
210 mEglSwapBuffersWithDamageKHR = reinterpret_cast<PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC>(eglGetProcAddress("eglSwapBuffersWithDamageKHR"));
211 if (!mEglSwapBuffersWithDamageKHR)
213 DALI_LOG_ERROR("Coudn't find eglSwapBuffersWithDamageKHR!\n");
214 mPartialUpdateRequired = false;
219 bool EglImplementation::CreateWindowContext( EGLContext& eglContext )
221 // make sure a context isn't created twice
222 DALI_ASSERT_ALWAYS( (eglContext == 0) && "EGL context recreated" );
224 eglContext = eglCreateContext(mEglDisplay, mEglConfig, mEglContext, &(mContextAttribs[0]));
225 TEST_EGL_ERROR("eglCreateContext render thread");
227 DALI_ASSERT_ALWAYS( EGL_NO_CONTEXT != eglContext && "EGL context not created" );
229 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VENDOR : %s ***\n", glGetString(GL_VENDOR));
230 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_RENDERER : %s ***\n", glGetString(GL_RENDERER));
231 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VERSION : %s ***\n", glGetString(GL_VERSION));
232 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_SHADING_LANGUAGE_VERSION : %s***\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
233 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** Supported Extensions ***\n%s\n\n", glGetString(GL_EXTENSIONS));
235 mEglWindowContexts.push_back( eglContext );
237 mEglSetDamageRegionKHR = reinterpret_cast<PFNEGLSETDAMAGEREGIONKHRPROC>(eglGetProcAddress("eglSetDamageRegionKHR"));
238 if (!mEglSetDamageRegionKHR)
240 DALI_LOG_ERROR("Coudn't find eglSetDamageRegionKHR!\n");
241 mPartialUpdateRequired = false;
243 mEglSwapBuffersWithDamageKHR = reinterpret_cast<PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC>(eglGetProcAddress("eglSwapBuffersWithDamageKHR"));
244 if (!mEglSwapBuffersWithDamageKHR)
246 DALI_LOG_ERROR("Coudn't find eglSwapBuffersWithDamageKHR!\n");
247 mPartialUpdateRequired = false;
252 void EglImplementation::DestroyContext( EGLContext& eglContext )
256 eglDestroyContext( mEglDisplay, eglContext );
261 void EglImplementation::DestroySurface( EGLSurface& eglSurface )
263 if(mIsOwnSurface && eglSurface)
265 // Make context null to prevent crash in driver side
267 eglDestroySurface( mEglDisplay, eglSurface );
272 void EglImplementation::MakeContextCurrent( EGLSurface eglSurface, EGLContext eglContext )
274 if (mCurrentEglContext == eglContext)
279 mCurrentEglSurface = eglSurface;
283 eglMakeCurrent( mEglDisplay, eglSurface, eglSurface, eglContext );
285 mCurrentEglContext = eglContext;
288 EGLint error = eglGetError();
290 if ( error != EGL_SUCCESS )
292 Egl::PrintError(error);
294 DALI_ASSERT_ALWAYS(false && "MakeContextCurrent failed!");
298 void EglImplementation::MakeCurrent( EGLNativePixmapType pixmap, EGLSurface eglSurface )
300 if (mCurrentEglContext == mEglContext)
305 mCurrentEglNativePixmap = pixmap;
306 mCurrentEglSurface = eglSurface;
310 eglMakeCurrent( mEglDisplay, eglSurface, eglSurface, mEglContext );
312 mCurrentEglContext = mEglContext;
315 EGLint error = eglGetError();
317 if ( error != EGL_SUCCESS )
319 Egl::PrintError(error);
321 DALI_ASSERT_ALWAYS(false && "MakeCurrent failed!");
325 void EglImplementation::MakeContextNull()
327 // clear the current context
328 eglMakeCurrent( mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
329 mCurrentEglContext = EGL_NO_CONTEXT;
332 void EglImplementation::TerminateGles()
334 if ( mGlesInitialized )
336 // Make context null to prevent crash in driver side
339 for ( auto eglSurface : mEglWindowSurfaces )
341 if(mIsOwnSurface && eglSurface)
343 eglDestroySurface(mEglDisplay, eglSurface);
346 eglDestroyContext(mEglDisplay, mEglContext);
347 for ( auto eglContext : mEglWindowContexts )
349 eglDestroyContext(mEglDisplay, eglContext);
352 eglTerminate(mEglDisplay);
357 mCurrentEglSurface = NULL;
358 mCurrentEglContext = EGL_NO_CONTEXT;
360 mGlesInitialized = false;
364 bool EglImplementation::IsGlesInitialized() const
366 return mGlesInitialized;
369 void EglImplementation::SwapBuffers( EGLSurface& eglSurface )
371 if ( eglSurface != EGL_NO_SURFACE ) // skip if using surfaceless context
373 #ifndef DALI_PROFILE_UBUNTU
374 if( mSwapBufferCountAfterResume < THRESHOLD_SWAPBUFFER_COUNT )
376 DALI_LOG_RELEASE_INFO( "EglImplementation::SwapBuffers started.\n" );
378 #endif //DALI_PROFILE_UBUNTU
380 // DALI_LOG_ERROR("EglImplementation::SwapBuffers()\n");
381 eglSwapBuffers( mEglDisplay, eglSurface );
383 #ifndef DALI_PROFILE_UBUNTU
384 if( mSwapBufferCountAfterResume < THRESHOLD_SWAPBUFFER_COUNT )
386 DALI_LOG_RELEASE_INFO( "EglImplementation::SwapBuffers finished.\n" );
387 mSwapBufferCountAfterResume++;
389 #endif //DALI_PROFILE_UBUNTU
393 EGLint EglImplementation::GetBufferAge(EGLSurface& eglSurface) const
396 eglQuerySurface(mEglDisplay, eglSurface, EGL_BUFFER_AGE_EXT, &age);
399 DALI_LOG_ERROR("eglQuerySurface(%d)\n", eglGetError());
403 // 0 - invalid buffer
407 DALI_LOG_ERROR("EglImplementation::GetBufferAge() buffer age %d > 3\n", age);
408 age = 0; // shoudn't be more than 3 back buffers, if there is just reset, I don't want to add extra history level
414 void EglImplementation::SetDamageRegion( EGLSurface& eglSurface, std::vector< Rect< int > >& damagedRects )
416 if( !mPartialUpdateRequired )
421 if( eglSurface != EGL_NO_SURFACE ) // skip if using surfaceless context
423 EGLBoolean result = mEglSetDamageRegionKHR( mEglDisplay, eglSurface, reinterpret_cast< int* >( damagedRects.data() ), 1 );
424 if (result == EGL_FALSE)
426 DALI_LOG_ERROR( "eglSetDamageRegionKHR(%d)\n", eglGetError() );
431 void EglImplementation::SwapBuffers(EGLSurface& eglSurface, const std::vector<Rect<int>>& damagedRects)
433 if (eglSurface != EGL_NO_SURFACE ) // skip if using surfaceless context
435 if (!mPartialUpdateRequired )
437 SwapBuffers(eglSurface);
441 #ifndef DALI_PROFILE_UBUNTU
442 if( mSwapBufferCountAfterResume < THRESHOLD_SWAPBUFFER_COUNT )
444 DALI_LOG_RELEASE_INFO( "EglImplementation::SwapBuffers started.\n" );
446 #endif //DALI_PROFILE_UBUNTU
448 EGLBoolean result = mEglSwapBuffersWithDamageKHR(mEglDisplay, eglSurface, reinterpret_cast<int*>(const_cast< std::vector< Rect< int > >& >( damagedRects ).data()), damagedRects.size());
449 if (result == EGL_FALSE)
451 DALI_LOG_ERROR("eglSwapBuffersWithDamageKHR(%d)\n", eglGetError());
454 #ifndef DALI_PROFILE_UBUNTU
455 if( mSwapBufferCountAfterResume < THRESHOLD_SWAPBUFFER_COUNT )
457 DALI_LOG_RELEASE_INFO( "EglImplementation::SwapBuffers finished.\n" );
458 mSwapBufferCountAfterResume++;
460 #endif //DALI_PROFILE_UBUNTU
464 void EglImplementation::CopyBuffers( EGLSurface& eglSurface )
466 eglCopyBuffers( mEglDisplay, eglSurface, mCurrentEglNativePixmap );
469 void EglImplementation::WaitGL()
474 bool EglImplementation::ChooseConfig( bool isWindowType, ColorDepth depth )
476 if(mEglConfig && isWindowType == mIsWindow && mColorDepth == depth)
482 mIsWindow = isWindowType;
485 Vector<EGLint> configAttribs;
486 configAttribs.Reserve(31);
490 configAttribs.PushBack( EGL_SURFACE_TYPE );
491 configAttribs.PushBack( EGL_WINDOW_BIT );
495 configAttribs.PushBack( EGL_SURFACE_TYPE );
496 configAttribs.PushBack( EGL_PIXMAP_BIT );
499 configAttribs.PushBack( EGL_RENDERABLE_TYPE );
501 if( mGlesVersion >= 30 )
503 configAttribs.PushBack( EGL_OPENGL_ES3_BIT_KHR );
507 configAttribs.PushBack( EGL_OPENGL_ES2_BIT );
510 // TODO: enable this flag when it becomes supported
511 // configAttribs.PushBack( EGL_CONTEXT_FLAGS_KHR );
512 // configAttribs.PushBack( EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR );
514 configAttribs.PushBack( EGL_RED_SIZE );
515 configAttribs.PushBack( 8 );
516 configAttribs.PushBack( EGL_GREEN_SIZE );
517 configAttribs.PushBack( 8 );
518 configAttribs.PushBack( EGL_BLUE_SIZE );
519 configAttribs.PushBack( 8 );
521 // For underlay video playback, we also need to set the alpha value of the 24/32bit window.
522 configAttribs.PushBack( EGL_ALPHA_SIZE );
523 configAttribs.PushBack( 8 );
525 configAttribs.PushBack( EGL_DEPTH_SIZE );
526 configAttribs.PushBack( mDepthBufferRequired ? 24 : 0 );
527 configAttribs.PushBack( EGL_STENCIL_SIZE );
528 configAttribs.PushBack( mStencilBufferRequired ? 8 : 0 );
530 #ifndef DALI_PROFILE_UBUNTU
531 if( mMultiSamplingLevel != EGL_DONT_CARE )
533 configAttribs.PushBack( EGL_SAMPLES );
534 configAttribs.PushBack( mMultiSamplingLevel );
535 configAttribs.PushBack( EGL_SAMPLE_BUFFERS );
536 configAttribs.PushBack( 1 );
538 #endif // DALI_PROFILE_UBUNTU
539 configAttribs.PushBack( EGL_NONE );
541 // Ensure number of configs is set to 1 as on some drivers,
542 // eglChooseConfig succeeds but does not actually create a proper configuration.
543 if ( ( eglChooseConfig( mEglDisplay, &(configAttribs[0]), &mEglConfig, 1, &numConfigs ) != EGL_TRUE ) ||
544 ( numConfigs != 1 ) )
546 if( mGlesVersion >= 30 )
549 DALI_LOG_ERROR("Fail to use OpenGL es 3.0. Retrying to use OpenGL es 2.0.");
553 if ( numConfigs != 1 )
555 DALI_LOG_ERROR("No configurations found.\n");
557 TEST_EGL_ERROR("eglChooseConfig");
560 EGLint error = eglGetError();
563 case EGL_BAD_DISPLAY:
565 DALI_LOG_ERROR("Display is not an EGL display connection\n");
568 case EGL_BAD_ATTRIBUTE:
570 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");
573 case EGL_NOT_INITIALIZED:
575 DALI_LOG_ERROR("Display has not been initialized\n");
578 case EGL_BAD_PARAMETER:
580 DALI_LOG_ERROR("The parameter numConfig is NULL\n");
585 DALI_LOG_ERROR("Unknown error.\n");
588 DALI_ASSERT_ALWAYS(false && "eglChooseConfig failed!");
591 Integration::Log::LogMessage(Integration::Log::DebugInfo, "Using OpenGL es %d.%d.\n", mGlesVersion / 10, mGlesVersion % 10 );
593 mContextAttribs.Clear();
594 if( mIsKhrCreateContextSupported )
596 mContextAttribs.Reserve(5);
597 mContextAttribs.PushBack( EGL_CONTEXT_MAJOR_VERSION_KHR );
598 mContextAttribs.PushBack( mGlesVersion / 10 );
599 mContextAttribs.PushBack( EGL_CONTEXT_MINOR_VERSION_KHR );
600 mContextAttribs.PushBack( mGlesVersion % 10 );
604 mContextAttribs.Reserve(3);
605 mContextAttribs.PushBack( EGL_CONTEXT_CLIENT_VERSION );
606 mContextAttribs.PushBack( mGlesVersion / 10 );
608 mContextAttribs.PushBack( EGL_NONE );
613 EGLSurface EglImplementation::CreateSurfaceWindow( EGLNativeWindowType window, ColorDepth depth )
615 mEglNativeWindow = window;
620 ChooseConfig(mIsWindow, mColorDepth);
622 mCurrentEglSurface = eglCreateWindowSurface( mEglDisplay, mEglConfig, mEglNativeWindow, NULL );
623 TEST_EGL_ERROR("eglCreateWindowSurface");
625 DALI_ASSERT_ALWAYS( mCurrentEglSurface && "Create window surface failed" );
627 return mCurrentEglSurface;
630 EGLSurface EglImplementation::CreateSurfacePixmap( EGLNativePixmapType pixmap, ColorDepth depth )
632 mCurrentEglNativePixmap = pixmap;
637 ChooseConfig(mIsWindow, mColorDepth);
639 mCurrentEglSurface = eglCreatePixmapSurface( mEglDisplay, mEglConfig, mCurrentEglNativePixmap, NULL );
640 TEST_EGL_ERROR("eglCreatePixmapSurface");
642 DALI_ASSERT_ALWAYS( mCurrentEglSurface && "Create pixmap surface failed" );
644 return mCurrentEglSurface;
647 bool EglImplementation::ReplaceSurfaceWindow( EGLNativeWindowType window, EGLSurface& eglSurface, EGLContext& eglContext )
649 bool contextLost = false;
651 // display connection has not changed, then we can just create a new surface
652 // the surface is bound to the context, so set the context to null
657 // destroy the surface
658 DestroySurface( eglSurface );
661 // create the EGL surface
662 EGLSurface newEglSurface = CreateSurfaceWindow( window, mColorDepth );
664 // set the context to be current with the new surface
665 MakeContextCurrent( newEglSurface, eglContext );
670 bool EglImplementation::ReplaceSurfacePixmap( EGLNativePixmapType pixmap, EGLSurface& eglSurface )
672 bool contextLost = false;
674 // display connection has not changed, then we can just create a new surface
675 // create the EGL surface
676 eglSurface = CreateSurfacePixmap( pixmap, mColorDepth );
678 // set the eglSurface to be current
679 MakeCurrent( pixmap, eglSurface );
684 void EglImplementation::SetGlesVersion( const int32_t glesVersion )
686 mGlesVersion = glesVersion;
689 void EglImplementation::SetFirstFrameAfterResume()
691 mSwapBufferCountAfterResume = 0;
694 EGLDisplay EglImplementation::GetDisplay() const
699 EGLContext EglImplementation::GetContext() const
704 int32_t EglImplementation::GetGlesVersion() const
709 bool EglImplementation::IsSurfacelessContextSupported() const
711 return mIsSurfacelessContextSupported;
714 void EglImplementation::WaitClient()
716 // Wait for EGL to finish executing all rendering calls for the current context
717 if ( eglWaitClient() != EGL_TRUE )
719 TEST_EGL_ERROR("eglWaitClient");
723 bool EglImplementation::IsPartialUpdateRequired() const
725 return mPartialUpdateRequired;
728 } // namespace Adaptor
730 } // namespace Internal
734 #pragma GCC diagnostic pop