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