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