6580a6fd3df4e8d80792f596610b0870e064e7c0
[platform/core/uifw/dali-adaptor.git] / dali / internal / graphics / gles / egl-implementation.cpp
1 /*
2  * Copyright (c) 2019 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18
19 // CLASS HEADER
20 #include <dali/internal/graphics/gles/egl-implementation.h>
21
22 // EXTERNAL INCLUDES
23 #include <dali/integration-api/debug.h>
24 #include <dali/public-api/common/dali-vector.h>
25
26 // INTERNAL INCLUDES
27 #include <dali/public-api/dali-adaptor-common.h>
28 #include <dali/internal/graphics/gles/gl-implementation.h>
29 #include <dali/internal/graphics/gles/egl-debug.h>
30
31 // EGL constants use C style casts
32 #pragma GCC diagnostic push
33 #pragma GCC diagnostic ignored "-Wold-style-cast"
34
35 namespace Dali
36 {
37
38 namespace Internal
39 {
40
41 namespace Adaptor
42 {
43
44 #define TEST_EGL_ERROR(lastCommand) \
45 { \
46   EGLint err = eglGetError(); \
47   if (err != EGL_SUCCESS) \
48   { \
49     DALI_LOG_ERROR("EGL error after %s\n", lastCommand); \
50     Egl::PrintError(err); \
51     DALI_ASSERT_ALWAYS(0 && "EGL error"); \
52   } \
53 }
54
55 EglImplementation::EglImplementation( int multiSamplingLevel,
56                                       Integration::DepthBufferAvailable depthBufferRequired,
57                                       Integration::StencilBufferAvailable stencilBufferRequired )
58 : mContextAttribs(),
59   mEglNativeDisplay( 0 ),
60   mEglNativeWindow( 0 ),
61   mCurrentEglNativePixmap( 0 ),
62   mEglDisplay( 0 ),
63   mEglConfig( 0 ),
64   mEglContext( 0 ),
65   mCurrentEglSurface( 0 ),
66   mCurrentEglContext( EGL_NO_CONTEXT ),
67   mMultiSamplingLevel( multiSamplingLevel ),
68   mGlesVersion( 30 ),
69   mColorDepth( COLOR_DEPTH_24 ),
70   mGlesInitialized( false ),
71   mIsOwnSurface( true ),
72   mIsWindow( true ),
73   mDepthBufferRequired( depthBufferRequired == Integration::DepthBufferAvailable::TRUE ),
74   mStencilBufferRequired( stencilBufferRequired == Integration::StencilBufferAvailable::TRUE )
75 {
76 }
77
78 EglImplementation::~EglImplementation()
79 {
80   TerminateGles();
81 }
82
83 bool EglImplementation::InitializeGles( EGLNativeDisplayType display, bool isOwnSurface )
84 {
85   if ( !mGlesInitialized )
86   {
87     mEglNativeDisplay = display;
88
89     //@todo see if we can just EGL_DEFAULT_DISPLAY instead
90     mEglDisplay = eglGetDisplay(mEglNativeDisplay);
91     EGLint error = eglGetError();
92
93     if( mEglDisplay == NULL && error != EGL_SUCCESS )
94     {
95       throw Dali::DaliException( "", "OpenGL ES is not supported");
96     }
97
98     EGLint majorVersion = 0;
99     EGLint minorVersion = 0;
100     if ( !eglInitialize( mEglDisplay, &majorVersion, &minorVersion ) )
101     {
102       return false;
103     }
104     eglBindAPI(EGL_OPENGL_ES_API);
105
106     mGlesInitialized = true;
107     mIsOwnSurface = isOwnSurface;
108   }
109
110   // We want to display this information all the time, so use the LogMessage directly
111   Integration::Log::LogMessage(Integration::Log::DebugInfo, "EGL Information\n"
112       "            Vendor:        %s\n"
113       "            Version:       %s\n"
114       "            Client APIs:   %s\n"
115       "            Extensions:    %s\n",
116       eglQueryString( mEglDisplay, EGL_VENDOR ),
117       eglQueryString( mEglDisplay, EGL_VERSION ),
118       eglQueryString( mEglDisplay, EGL_CLIENT_APIS ),
119       eglQueryString( mEglDisplay, EGL_EXTENSIONS ));
120
121   return mGlesInitialized;
122 }
123
124 bool EglImplementation::CreateContext()
125 {
126   // make sure a context isn't created twice
127   DALI_ASSERT_ALWAYS( (mEglContext == 0) && "EGL context recreated" );
128
129   mEglContext = eglCreateContext(mEglDisplay, mEglConfig, NULL, &(mContextAttribs[0]));
130   TEST_EGL_ERROR("eglCreateContext render thread");
131
132   DALI_ASSERT_ALWAYS( EGL_NO_CONTEXT != mEglContext && "EGL context not created" );
133
134   DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VENDOR : %s ***\n", glGetString(GL_VENDOR));
135   DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_RENDERER : %s ***\n", glGetString(GL_RENDERER));
136   DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VERSION : %s ***\n", glGetString(GL_VERSION));
137   DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_SHADING_LANGUAGE_VERSION : %s***\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
138   DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** Supported Extensions ***\n%s\n\n", glGetString(GL_EXTENSIONS));
139
140   return true;
141 }
142
143 bool EglImplementation::CreateWindowContext( EGLContext& eglContext )
144 {
145   // make sure a context isn't created twice
146   DALI_ASSERT_ALWAYS( (eglContext == 0) && "EGL context recreated" );
147
148   eglContext = eglCreateContext(mEglDisplay, mEglConfig, mEglContext, &(mContextAttribs[0]));
149   TEST_EGL_ERROR("eglCreateContext render thread");
150
151   DALI_ASSERT_ALWAYS( EGL_NO_CONTEXT != eglContext && "EGL context not created" );
152
153   DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VENDOR : %s ***\n", glGetString(GL_VENDOR));
154   DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_RENDERER : %s ***\n", glGetString(GL_RENDERER));
155   DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VERSION : %s ***\n", glGetString(GL_VERSION));
156   DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_SHADING_LANGUAGE_VERSION : %s***\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
157   DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** Supported Extensions ***\n%s\n\n", glGetString(GL_EXTENSIONS));
158
159   mEglWindowContexts.push_back( eglContext );
160
161   return true;
162 }
163
164 void EglImplementation::DestroyContext( EGLContext& eglContext )
165 {
166   DALI_ASSERT_ALWAYS( mEglContext && "no EGL context" );
167
168   eglDestroyContext( mEglDisplay, eglContext );
169   eglContext = 0;
170 }
171
172 void EglImplementation::DestroySurface( EGLSurface& eglSurface )
173 {
174   if(mIsOwnSurface && eglSurface)
175   {
176     // Make context null to prevent crash in driver side
177     MakeContextNull();
178     eglDestroySurface( mEglDisplay, eglSurface );
179     eglSurface = 0;
180   }
181 }
182
183 void EglImplementation::MakeContextCurrent( EGLSurface eglSurface, EGLContext eglContext )
184 {
185   if (mCurrentEglContext == eglContext)
186   {
187     return;
188   }
189
190   mCurrentEglSurface = eglSurface;
191
192   if(mIsOwnSurface)
193   {
194     glFinish();
195
196     eglMakeCurrent( mEglDisplay, eglSurface, eglSurface, eglContext );
197
198     mCurrentEglContext = eglContext;
199   }
200
201   EGLint error = eglGetError();
202
203   if ( error != EGL_SUCCESS )
204   {
205     Egl::PrintError(error);
206
207     DALI_ASSERT_ALWAYS(false && "MakeContextCurrent failed!");
208   }
209 }
210
211 void EglImplementation::MakeCurrent( EGLNativePixmapType pixmap, EGLSurface eglSurface )
212 {
213   if (mCurrentEglContext == mEglContext)
214   {
215     return;
216   }
217
218   mCurrentEglNativePixmap = pixmap;
219   mCurrentEglSurface = eglSurface;
220
221   if(mIsOwnSurface)
222   {
223     glFinish();
224
225     eglMakeCurrent( mEglDisplay, eglSurface, eglSurface, mEglContext );
226
227     mCurrentEglContext = mEglContext;
228   }
229
230   EGLint error = eglGetError();
231
232   if ( error != EGL_SUCCESS )
233   {
234     Egl::PrintError(error);
235
236     DALI_ASSERT_ALWAYS(false && "MakeCurrent failed!");
237   }
238 }
239
240 void EglImplementation::MakeContextNull()
241 {
242   // clear the current context
243   eglMakeCurrent( mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
244   mCurrentEglContext = EGL_NO_CONTEXT;
245 }
246
247 void EglImplementation::TerminateGles()
248 {
249   if ( mGlesInitialized )
250   {
251     // Make context null to prevent crash in driver side
252     MakeContextNull();
253
254     for ( auto eglSurface : mEglWindowSurfaces )
255     {
256       if(mIsOwnSurface && eglSurface)
257       {
258         eglDestroySurface(mEglDisplay, eglSurface);
259       }
260     }
261     eglDestroyContext(mEglDisplay, mEglContext);
262     for ( auto eglContext : mEglWindowContexts )
263     {
264       eglDestroyContext(mEglDisplay, eglContext);
265     }
266
267     eglTerminate(mEglDisplay);
268
269     mEglDisplay = NULL;
270     mEglConfig  = NULL;
271     mEglContext = NULL;
272     mCurrentEglSurface = NULL;
273     mCurrentEglContext = EGL_NO_CONTEXT;
274
275     mGlesInitialized = false;
276   }
277 }
278
279 bool EglImplementation::IsGlesInitialized() const
280 {
281   return mGlesInitialized;
282 }
283
284 void EglImplementation::SwapBuffers( EGLSurface& eglSurface )
285 {
286   if ( eglSurface != EGL_NO_SURFACE ) // skip if using surfaceless context
287   {
288     eglSwapBuffers( mEglDisplay, eglSurface );
289   }
290 }
291
292 void EglImplementation::CopyBuffers( EGLSurface& eglSurface )
293 {
294   eglCopyBuffers( mEglDisplay, eglSurface, mCurrentEglNativePixmap );
295 }
296
297 void EglImplementation::WaitGL()
298 {
299   eglWaitGL();
300 }
301
302 bool EglImplementation::ChooseConfig( bool isWindowType, ColorDepth depth )
303 {
304   if(mEglConfig && isWindowType == mIsWindow && mColorDepth == depth)
305   {
306     return true;
307   }
308
309   bool isTransparent = ( depth == COLOR_DEPTH_32 );
310
311   mColorDepth = depth;
312   mIsWindow = isWindowType;
313
314   EGLint numConfigs;
315   Vector<EGLint> configAttribs;
316   configAttribs.Reserve(31);
317
318   if(isWindowType)
319   {
320     configAttribs.PushBack( EGL_SURFACE_TYPE );
321     configAttribs.PushBack( EGL_WINDOW_BIT );
322   }
323   else
324   {
325     configAttribs.PushBack( EGL_SURFACE_TYPE );
326     configAttribs.PushBack( EGL_PIXMAP_BIT );
327   }
328
329   configAttribs.PushBack( EGL_RENDERABLE_TYPE );
330
331   if( mGlesVersion >= 30 )
332   {
333 #ifdef _ARCH_ARM_
334     configAttribs.PushBack( EGL_OPENGL_ES3_BIT_KHR );
335 #else
336     configAttribs.PushBack( EGL_OPENGL_ES2_BIT );
337 #endif // _ARCH_ARM_
338   }
339   else
340   {
341     configAttribs.PushBack( EGL_OPENGL_ES2_BIT );
342   }
343
344 // TODO: enable this flag when it becomes supported
345 //  configAttribs.PushBack( EGL_CONTEXT_FLAGS_KHR );
346 //  configAttribs.PushBack( EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR );
347
348   configAttribs.PushBack( EGL_RED_SIZE );
349   configAttribs.PushBack( 8 );
350   configAttribs.PushBack( EGL_GREEN_SIZE );
351   configAttribs.PushBack( 8 );
352   configAttribs.PushBack( EGL_BLUE_SIZE );
353   configAttribs.PushBack( 8 );
354
355   if ( isTransparent )
356   {
357     configAttribs.PushBack( EGL_ALPHA_SIZE );
358 #ifdef _ARCH_ARM_
359     // For underlay video playback, we also need to set the alpha value of the 24/32bit window.
360     configAttribs.PushBack( 8 );
361 #else
362     // There is a bug in the desktop emulator
363     // setting EGL_ALPHA_SIZE to 8 results in eglChooseConfig failing
364     configAttribs.PushBack( 8 );
365 #endif // _ARCH_ARM_
366   }
367
368   configAttribs.PushBack( EGL_DEPTH_SIZE );
369   configAttribs.PushBack( mDepthBufferRequired ? 24 : 0 );
370   configAttribs.PushBack( EGL_STENCIL_SIZE );
371   configAttribs.PushBack( mStencilBufferRequired ? 8 : 0 );
372
373 #ifndef DALI_PROFILE_UBUNTU
374   if( mMultiSamplingLevel != EGL_DONT_CARE )
375   {
376     configAttribs.PushBack( EGL_SAMPLES );
377     configAttribs.PushBack( mMultiSamplingLevel );
378     configAttribs.PushBack( EGL_SAMPLE_BUFFERS );
379     configAttribs.PushBack( 1 );
380   }
381 #endif // DALI_PROFILE_UBUNTU
382   configAttribs.PushBack( EGL_NONE );
383
384   if ( eglChooseConfig( mEglDisplay, &(configAttribs[0]), &mEglConfig, 1, &numConfigs ) != EGL_TRUE )
385   {
386     if( mGlesVersion >= 30 )
387     {
388       mEglConfig = NULL;
389       DALI_LOG_ERROR("Fail to use OpenGL es 3.0. Retring to use OpenGL es 2.0.");
390       return false;
391     }
392
393     EGLint error = eglGetError();
394     switch (error)
395     {
396       case EGL_BAD_DISPLAY:
397       {
398         DALI_LOG_ERROR("Display is not an EGL display connection\n");
399         break;
400       }
401       case EGL_BAD_ATTRIBUTE:
402       {
403         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");
404         break;
405       }
406       case EGL_NOT_INITIALIZED:
407       {
408         DALI_LOG_ERROR("Display has not been initialized\n");
409         break;
410       }
411       case EGL_BAD_PARAMETER:
412       {
413         DALI_LOG_ERROR("The parameter numConfig is NULL\n");
414         break;
415       }
416       default:
417       {
418         DALI_LOG_ERROR("Unknown error.\n");
419       }
420     }
421     DALI_ASSERT_ALWAYS(false && "eglChooseConfig failed!");
422     return false;
423   }
424   Integration::Log::LogMessage(Integration::Log::DebugInfo, "Using OpenGL es %d.%d.\n", mGlesVersion / 10, mGlesVersion % 10 );
425
426   mContextAttribs.Clear();
427   if( mGlesVersion >= 30 )
428   {
429     mContextAttribs.Reserve(5);
430     mContextAttribs.PushBack( EGL_CONTEXT_MAJOR_VERSION_KHR );
431     mContextAttribs.PushBack( mGlesVersion / 10 );
432     mContextAttribs.PushBack( EGL_CONTEXT_MINOR_VERSION_KHR );
433     mContextAttribs.PushBack( mGlesVersion % 10 );
434   }
435   else
436   {
437     mContextAttribs.Reserve(3);
438     mContextAttribs.PushBack( EGL_CONTEXT_CLIENT_VERSION );
439     mContextAttribs.PushBack( 2 );
440   }
441   mContextAttribs.PushBack( EGL_NONE );
442
443   if ( numConfigs != 1 )
444   {
445     DALI_LOG_ERROR("No configurations found.\n");
446
447     TEST_EGL_ERROR("eglChooseConfig");
448   }
449
450   return true;
451 }
452
453 EGLSurface EglImplementation::CreateSurfaceWindow( EGLNativeWindowType window, ColorDepth depth )
454 {
455   mEglNativeWindow = window;
456   mColorDepth = depth;
457   mIsWindow = true;
458
459   // egl choose config
460   ChooseConfig(mIsWindow, mColorDepth);
461
462   mCurrentEglSurface = eglCreateWindowSurface( mEglDisplay, mEglConfig, mEglNativeWindow, NULL );
463   TEST_EGL_ERROR("eglCreateWindowSurface");
464
465   DALI_ASSERT_ALWAYS( mCurrentEglSurface && "Create window surface failed" );
466
467   return mCurrentEglSurface;
468 }
469
470 EGLSurface EglImplementation::CreateSurfacePixmap( EGLNativePixmapType pixmap, ColorDepth depth )
471 {
472   mCurrentEglNativePixmap = pixmap;
473   mColorDepth = depth;
474   mIsWindow = false;
475
476   // egl choose config
477   ChooseConfig(mIsWindow, mColorDepth);
478
479   mCurrentEglSurface = eglCreatePixmapSurface( mEglDisplay, mEglConfig, mCurrentEglNativePixmap, NULL );
480   TEST_EGL_ERROR("eglCreatePixmapSurface");
481
482   DALI_ASSERT_ALWAYS( mCurrentEglSurface && "Create pixmap surface failed" );
483
484   return mCurrentEglSurface;
485 }
486
487 bool EglImplementation::ReplaceSurfaceWindow( EGLNativeWindowType window, EGLSurface& eglSurface, EGLContext& eglContext )
488 {
489   bool contextLost = false;
490
491   // display connection has not changed, then we can just create a new surface
492   //  the surface is bound to the context, so set the context to null
493   MakeContextNull();
494
495   // destroy the surface
496   DestroySurface( eglSurface );
497
498   // create the EGL surface
499   CreateSurfaceWindow( window, mColorDepth );
500
501   // set the context to be current with the new surface
502   MakeContextCurrent( eglSurface, eglContext );
503
504   return contextLost;
505 }
506
507 bool EglImplementation::ReplaceSurfacePixmap( EGLNativePixmapType pixmap, EGLSurface& eglSurface )
508 {
509   bool contextLost = false;
510
511   // display connection has not changed, then we can just create a new surface
512   // create the EGL surface
513   eglSurface = CreateSurfacePixmap( pixmap, mColorDepth );
514
515   // set the eglSurface to be current
516   MakeCurrent( pixmap, eglSurface );
517
518   return contextLost;
519 }
520
521 void EglImplementation::SetGlesVersion( const int32_t glesVersion )
522 {
523   mGlesVersion = glesVersion;
524 }
525
526 EGLDisplay EglImplementation::GetDisplay() const
527 {
528   return mEglDisplay;
529 }
530
531 EGLContext EglImplementation::GetContext() const
532 {
533   return mEglContext;
534 }
535
536 int32_t EglImplementation::GetGlesVersion() const
537 {
538   return mGlesVersion;
539 }
540
541 } // namespace Adaptor
542
543 } // namespace Internal
544
545 } // namespace Dali
546
547 #pragma GCC diagnostic pop