Force the completion of all GL executions before switching the context
[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   mCurrentEglContext( EGL_NO_CONTEXT ),
68   mMultiSamplingLevel( multiSamplingLevel ),
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     mContextAttribs.Clear();
107
108 #if DALI_GLES_VERSION >= 30
109
110     mContextAttribs.Reserve(5);
111     mContextAttribs.PushBack( EGL_CONTEXT_MAJOR_VERSION_KHR );
112     mContextAttribs.PushBack( DALI_GLES_VERSION / 10 );
113     mContextAttribs.PushBack( EGL_CONTEXT_MINOR_VERSION_KHR );
114     mContextAttribs.PushBack( DALI_GLES_VERSION % 10 );
115
116 #else // DALI_GLES_VERSION >= 30
117
118     mContextAttribs.Reserve(3);
119     mContextAttribs.PushBack( EGL_CONTEXT_CLIENT_VERSION );
120     mContextAttribs.PushBack( 2 );
121
122 #endif // DALI_GLES_VERSION >= 30
123
124     mContextAttribs.PushBack( EGL_NONE );
125
126     mGlesInitialized = true;
127     mIsOwnSurface = isOwnSurface;
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       eglQueryString( mEglDisplay, EGL_EXTENSIONS ));
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 void EglImplementation::ChooseConfig( bool isWindowType, ColorDepth depth )
323 {
324   if(mEglConfig && isWindowType == mIsWindow && mColorDepth == depth)
325   {
326     return;
327   }
328
329   bool isTransparent = ( depth == COLOR_DEPTH_32 );
330
331   mIsWindow = isWindowType;
332
333   EGLint numConfigs;
334   Vector<EGLint> configAttribs;
335   configAttribs.Reserve(31);
336
337   if(isWindowType)
338   {
339     configAttribs.PushBack( EGL_SURFACE_TYPE );
340     configAttribs.PushBack( EGL_WINDOW_BIT );
341   }
342   else
343   {
344     configAttribs.PushBack( EGL_SURFACE_TYPE );
345     configAttribs.PushBack( EGL_PIXMAP_BIT );
346   }
347
348   configAttribs.PushBack( EGL_RENDERABLE_TYPE );
349
350 #if DALI_GLES_VERSION >= 30
351
352 #ifdef _ARCH_ARM_
353   configAttribs.PushBack( EGL_OPENGL_ES3_BIT_KHR );
354 #else
355   // There is a bug in the desktop emulator
356   // Requesting for ES3 causes eglCreateContext even though it allows to ask
357   // for a configuration that supports GLES 3.0
358   configAttribs.PushBack( EGL_OPENGL_ES2_BIT );
359 #endif // _ARCH_ARM_
360
361 #else // DALI_GLES_VERSION >= 30
362
363   Integration::Log::LogMessage( Integration::Log::DebugInfo, "Using OpenGL ES 2 \n" );
364   configAttribs.PushBack( EGL_OPENGL_ES2_BIT );
365
366 #endif //DALI_GLES_VERSION >= 30
367
368 #if DALI_GLES_VERSION >= 30
369 // TODO: enable this flag when it becomes supported
370 //  configAttribs.PushBack( EGL_CONTEXT_FLAGS_KHR );
371 //  configAttribs.PushBack( EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR );
372 #endif //DALI_GLES_VERSION >= 30
373
374   configAttribs.PushBack( EGL_RED_SIZE );
375   configAttribs.PushBack( 8 );
376   configAttribs.PushBack( EGL_GREEN_SIZE );
377   configAttribs.PushBack( 8 );
378   configAttribs.PushBack( EGL_BLUE_SIZE );
379   configAttribs.PushBack( 8 );
380
381   if ( isTransparent )
382   {
383     configAttribs.PushBack( EGL_ALPHA_SIZE );
384 #ifdef _ARCH_ARM_
385     // For underlay video playback, we also need to set the alpha value of the 24/32bit window.
386     configAttribs.PushBack( 8 );
387 #else
388     // There is a bug in the desktop emulator
389     // setting EGL_ALPHA_SIZE to 8 results in eglChooseConfig failing
390     configAttribs.PushBack( 8 );
391 #endif // _ARCH_ARM_
392   }
393
394   configAttribs.PushBack( EGL_DEPTH_SIZE );
395   configAttribs.PushBack( mDepthBufferRequired ? 24 : 0 );
396   configAttribs.PushBack( EGL_STENCIL_SIZE );
397   configAttribs.PushBack( mStencilBufferRequired ? 8 : 0 );
398
399 #ifndef DALI_PROFILE_UBUNTU
400   if( mMultiSamplingLevel != EGL_DONT_CARE )
401   {
402     configAttribs.PushBack( EGL_SAMPLES );
403     configAttribs.PushBack( mMultiSamplingLevel );
404     configAttribs.PushBack( EGL_SAMPLE_BUFFERS );
405     configAttribs.PushBack( 1 );
406   }
407 #endif // DALI_PROFILE_UBUNTU
408   configAttribs.PushBack( EGL_NONE );
409
410   if ( eglChooseConfig( mEglDisplay, &(configAttribs[0]), &mEglConfig, 1, &numConfigs ) != EGL_TRUE )
411   {
412     EGLint error = eglGetError();
413     switch (error)
414     {
415       case EGL_BAD_DISPLAY:
416       {
417         DALI_LOG_ERROR("Display is not an EGL display connection\n");
418         break;
419       }
420       case EGL_BAD_ATTRIBUTE:
421       {
422         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");
423         break;
424       }
425       case EGL_NOT_INITIALIZED:
426       {
427         DALI_LOG_ERROR("Display has not been initialized\n");
428         break;
429       }
430       case EGL_BAD_PARAMETER:
431       {
432         DALI_LOG_ERROR("The parameter numConfig is NULL\n");
433         break;
434       }
435       default:
436       {
437         DALI_LOG_ERROR("Unknown error.\n");
438       }
439     }
440     DALI_ASSERT_ALWAYS(false && "eglChooseConfig failed!");
441   }
442
443   if ( numConfigs != 1 )
444   {
445     DALI_LOG_ERROR("No configurations found.\n");
446
447     TEST_EGL_ERROR("eglChooseConfig");
448   }
449 }
450
451 EGLSurface EglImplementation::CreateSurfaceWindow( EGLNativeWindowType window, ColorDepth depth )
452 {
453   mEglNativeWindow = window;
454   mColorDepth = depth;
455   mIsWindow = true;
456
457   // egl choose config
458   ChooseConfig(mIsWindow, mColorDepth);
459
460   mCurrentEglSurface = eglCreateWindowSurface( mEglDisplay, mEglConfig, mEglNativeWindow, NULL );
461   TEST_EGL_ERROR("eglCreateWindowSurface");
462
463   DALI_ASSERT_ALWAYS( mCurrentEglSurface && "Create window surface failed" );
464
465   return mCurrentEglSurface;
466 }
467
468 EGLSurface EglImplementation::CreateSurfacePixmap( EGLNativePixmapType pixmap, ColorDepth depth )
469 {
470   mCurrentEglNativePixmap = pixmap;
471   mColorDepth = depth;
472   mIsWindow = false;
473
474   // egl choose config
475   ChooseConfig(mIsWindow, mColorDepth);
476
477   mCurrentEglSurface = eglCreatePixmapSurface( mEglDisplay, mEglConfig, mCurrentEglNativePixmap, NULL );
478   TEST_EGL_ERROR("eglCreatePixmapSurface");
479
480   DALI_ASSERT_ALWAYS( mCurrentEglSurface && "Create pixmap surface failed" );
481
482   return mCurrentEglSurface;
483 }
484
485 bool EglImplementation::ReplaceSurfaceWindow( EGLNativeWindowType window, EGLSurface& eglSurface, EGLContext& eglContext )
486 {
487   bool contextLost = false;
488
489   // display connection has not changed, then we can just create a new surface
490   //  the surface is bound to the context, so set the context to null
491   MakeContextNull();
492
493   // destroy the surface
494   DestroySurface( eglSurface );
495
496   // create the EGL surface
497   CreateSurfaceWindow( window, mColorDepth );
498
499   // set the context to be current with the new surface
500   MakeContextCurrent( eglSurface, eglContext );
501
502   return contextLost;
503 }
504
505 bool EglImplementation::ReplaceSurfacePixmap( EGLNativePixmapType pixmap, EGLSurface& eglSurface )
506 {
507   bool contextLost = false;
508
509   // display connection has not changed, then we can just create a new surface
510   // create the EGL surface
511   eglSurface = CreateSurfacePixmap( pixmap, mColorDepth );
512
513   // set the eglSurface to be current
514   MakeCurrent( pixmap, eglSurface );
515
516   return contextLost;
517 }
518
519 EGLDisplay EglImplementation::GetDisplay() const
520 {
521   return mEglDisplay;
522 }
523
524 EGLContext EglImplementation::GetContext() const
525 {
526   return mEglContext;
527 }
528
529 } // namespace Adaptor
530
531 } // namespace Internal
532
533 } // namespace Dali
534
535 #pragma GCC diagnostic pop