b458c2c73a235e8f27f71e4e3d947402bb21f628
[platform/core/uifw/dali-adaptor.git] / adaptors / common / gl / egl-implementation.cpp
1 /*
2  * Copyright (c) 2017 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 <gl/egl-implementation.h>
21
22 // EXTERNAL INCLUDES
23 #include <dali/integration-api/debug.h>
24 #include <dali/public-api/common/dali-common.h>
25 #include <dali/public-api/common/dali-vector.h>
26
27 // INTERNAL INCLUDES
28 #include <gl/gl-implementation.h>
29 #include <gl/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( unsigned int multiSamplingLevel )
56   : mEglNativeDisplay(0),
57     mEglNativeWindow(0),
58     mCurrentEglNativePixmap(0),
59     mEglDisplay(0),
60     mEglConfig(0),
61     mEglContext(0),
62     mCurrentEglSurface(0),
63     mGlesInitialized(false),
64     mIsOwnSurface(true),
65     mContextCurrent(false),
66     mIsWindow(true),
67     mColorDepth(COLOR_DEPTH_24),
68     mMultiSamplingLevel( multiSamplingLevel )
69 {
70 }
71
72 EglImplementation::~EglImplementation()
73 {
74   TerminateGles();
75 }
76
77 bool EglImplementation::InitializeGles( EGLNativeDisplayType display, bool isOwnSurface )
78 {
79   if ( !mGlesInitialized )
80   {
81     mEglNativeDisplay = display;
82
83     //@todo see if we can just EGL_DEFAULT_DISPLAY instead
84     mEglDisplay = eglGetDisplay(mEglNativeDisplay);
85     EGLint error = eglGetError();
86
87     if( mEglDisplay == NULL && error != EGL_SUCCESS )
88     {
89       throw Dali::DaliException( "", "OpenGL ES is not supported");
90     }
91
92     EGLint majorVersion = 0;
93     EGLint minorVersion = 0;
94     if ( !eglInitialize( mEglDisplay, &majorVersion, &minorVersion ) )
95     {
96       return false;
97     }
98     eglBindAPI(EGL_OPENGL_ES_API);
99
100     mContextAttribs.Clear();
101
102 #if DALI_GLES_VERSION >= 30
103
104     mContextAttribs.Reserve(5);
105     mContextAttribs.PushBack( EGL_CONTEXT_MAJOR_VERSION_KHR );
106     mContextAttribs.PushBack( DALI_GLES_VERSION / 10 );
107     mContextAttribs.PushBack( EGL_CONTEXT_MINOR_VERSION_KHR );
108     mContextAttribs.PushBack( DALI_GLES_VERSION % 10 );
109
110 #else // DALI_GLES_VERSION >= 30
111
112     mContextAttribs.Reserve(3);
113     mContextAttribs.PushBack( EGL_CONTEXT_CLIENT_VERSION );
114     mContextAttribs.PushBack( 2 );
115
116 #endif // DALI_GLES_VERSION >= 30
117
118     mContextAttribs.PushBack( EGL_NONE );
119
120     mGlesInitialized = true;
121     mIsOwnSurface = isOwnSurface;
122   }
123
124   return mGlesInitialized;
125 }
126
127 bool EglImplementation::CreateContext()
128 {
129   // make sure a context isn't created twice
130   DALI_ASSERT_ALWAYS( (mEglContext == 0) && "EGL context recreated" );
131
132   mEglContext = eglCreateContext(mEglDisplay, mEglConfig, NULL, &(mContextAttribs[0]));
133   TEST_EGL_ERROR("eglCreateContext render thread");
134
135   DALI_ASSERT_ALWAYS( EGL_NO_CONTEXT != mEglContext && "EGL context not created" );
136
137   DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VENDOR : %s ***\n", glGetString(GL_VENDOR));
138   DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_RENDERER : %s ***\n", glGetString(GL_RENDERER));
139   DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VERSION : %s ***\n", glGetString(GL_VERSION));
140   DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_SHADING_LANGUAGE_VERSION : %s***\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
141   DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** Supported Extensions ***\n%s\n\n", glGetString(GL_EXTENSIONS));
142
143   return true;
144 }
145
146 void EglImplementation::DestroyContext()
147 {
148   DALI_ASSERT_ALWAYS( mEglContext && "no EGL context" );
149
150   eglDestroyContext( mEglDisplay, mEglContext );
151   mEglContext = 0;
152 }
153
154 void EglImplementation::DestroySurface()
155 {
156   if(mIsOwnSurface && mCurrentEglSurface)
157   {
158     MakeContextNull();
159     eglDestroySurface( mEglDisplay, mCurrentEglSurface );
160     mCurrentEglSurface = 0;
161   }
162 }
163
164 void EglImplementation::MakeContextCurrent()
165 {
166   mContextCurrent = true;
167
168   if(mIsOwnSurface)
169   {
170     eglMakeCurrent( mEglDisplay, mCurrentEglSurface, mCurrentEglSurface, mEglContext );
171   }
172
173   EGLint error = eglGetError();
174
175   if ( error != EGL_SUCCESS )
176   {
177     Egl::PrintError(error);
178
179     DALI_ASSERT_ALWAYS(false && "MakeContextCurrent failed!");
180   }
181
182   // We want to display this information all the time, so use the LogMessage directly
183   Integration::Log::LogMessage(Integration::Log::DebugInfo, "EGL Information\n"
184       "            Vendor:        %s\n"
185       "            Version:       %s\n"
186       "            Client APIs:   %s\n"
187       "            Extensions:    %s\n",
188       eglQueryString(mEglDisplay, EGL_VENDOR),
189       eglQueryString(mEglDisplay, EGL_VERSION),
190       eglQueryString(mEglDisplay, EGL_CLIENT_APIS),
191       eglQueryString(mEglDisplay, EGL_EXTENSIONS));
192 }
193
194 void EglImplementation::MakeCurrent( EGLNativePixmapType pixmap, EGLSurface eglSurface )
195 {
196   mCurrentEglNativePixmap = pixmap;
197   mCurrentEglSurface = eglSurface;
198
199   if(mIsOwnSurface)
200   {
201     eglMakeCurrent( mEglDisplay, mCurrentEglSurface, mCurrentEglSurface, mEglContext );
202   }
203
204   EGLint error = eglGetError();
205
206   if ( error != EGL_SUCCESS )
207   {
208     Egl::PrintError(error);
209
210     DALI_ASSERT_ALWAYS(false && "MakeCurrent failed!");
211   }
212 }
213
214 void EglImplementation::MakeContextNull()
215 {
216   mContextCurrent = false;
217   // clear the current context
218   eglMakeCurrent( mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
219 }
220
221 void EglImplementation::TerminateGles()
222 {
223   if ( mGlesInitialized )
224   {
225     // in latest Mali DDK (r2p3 ~ r3p0 in April, 2012),
226     // MakeContextNull should be called before eglDestroy surface
227     // to prevent crash in _mali_surface_destroy_callback
228     MakeContextNull();
229
230     if(mIsOwnSurface && mCurrentEglSurface)
231     {
232       eglDestroySurface(mEglDisplay, mCurrentEglSurface);
233     }
234     eglDestroyContext(mEglDisplay, mEglContext);
235
236     eglTerminate(mEglDisplay);
237
238     mEglDisplay = NULL;
239     mEglConfig  = NULL;
240     mEglContext = NULL;
241     mCurrentEglSurface = NULL;
242
243     mGlesInitialized = false;
244   }
245 }
246
247 bool EglImplementation::IsGlesInitialized() const
248 {
249   return mGlesInitialized;
250 }
251
252 void EglImplementation::SwapBuffers()
253 {
254   eglSwapBuffers( mEglDisplay, mCurrentEglSurface );
255 }
256
257 void EglImplementation::CopyBuffers()
258 {
259   eglCopyBuffers( mEglDisplay, mCurrentEglSurface, mCurrentEglNativePixmap );
260 }
261
262 void EglImplementation::WaitGL()
263 {
264   eglWaitGL();
265 }
266
267 void EglImplementation::ChooseConfig( bool isWindowType, ColorDepth depth )
268 {
269   if(mEglConfig && isWindowType == mIsWindow && mColorDepth == depth)
270   {
271     return;
272   }
273
274   mIsWindow = isWindowType;
275
276   EGLint numConfigs;
277   Vector<EGLint> configAttribs;
278   configAttribs.Reserve(31);
279
280   if(isWindowType)
281   {
282     configAttribs.PushBack( EGL_SURFACE_TYPE );
283     configAttribs.PushBack( EGL_WINDOW_BIT );
284   }
285   else
286   {
287     configAttribs.PushBack( EGL_SURFACE_TYPE );
288     configAttribs.PushBack( EGL_PIXMAP_BIT );
289   }
290
291   configAttribs.PushBack( EGL_RENDERABLE_TYPE );
292
293 #if DALI_GLES_VERSION >= 30
294
295 #ifdef _ARCH_ARM_
296   configAttribs.PushBack( EGL_OPENGL_ES3_BIT_KHR );
297 #else
298   // There is a bug in the desktop emulator
299   // Requesting for ES3 causes eglCreateContext even though it allows to ask
300   // for a configuration that supports GLES 3.0
301   configAttribs.PushBack( EGL_OPENGL_ES2_BIT );
302 #endif // _ARCH_ARM_
303
304 #else // DALI_GLES_VERSION >= 30
305
306   Integration::Log::LogMessage( Integration::Log::DebugInfo, "Using OpenGL ES 2 \n" );
307   configAttribs.PushBack( EGL_OPENGL_ES2_BIT );
308
309 #endif //DALI_GLES_VERSION >= 30
310
311 #if DALI_GLES_VERSION >= 30
312 // TODO: enable this flag when it becomes supported
313 //  configAttribs.PushBack( EGL_CONTEXT_FLAGS_KHR );
314 //  configAttribs.PushBack( EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR );
315 #endif //DALI_GLES_VERSION >= 30
316
317   configAttribs.PushBack( EGL_RED_SIZE );
318   configAttribs.PushBack( 8 );
319   configAttribs.PushBack( EGL_GREEN_SIZE );
320   configAttribs.PushBack( 8 );
321   configAttribs.PushBack( EGL_BLUE_SIZE );
322   configAttribs.PushBack( 8 );
323
324   configAttribs.PushBack( EGL_ALPHA_SIZE );
325 #ifdef _ARCH_ARM_
326   configAttribs.PushBack( (depth == COLOR_DEPTH_32) ? 8 : 0 );
327 #else
328   // There is a bug in the desktop emulator
329   // setting EGL_ALPHA_SIZE to 8 results in eglChooseConfig failing
330   configAttribs.PushBack( 0 );
331 #endif // _ARCH_ARM_
332
333   configAttribs.PushBack( EGL_DEPTH_SIZE );
334   configAttribs.PushBack( 24 );
335   configAttribs.PushBack( EGL_STENCIL_SIZE );
336   configAttribs.PushBack( 8 );
337 #ifndef DALI_PROFILE_UBUNTU
338   configAttribs.PushBack( EGL_SAMPLES );
339   configAttribs.PushBack( mMultiSamplingLevel );
340   configAttribs.PushBack( EGL_SAMPLE_BUFFERS );
341   configAttribs.PushBack( 1 );
342 #endif // DALI_PROFILE_UBUNTU
343   configAttribs.PushBack( EGL_NONE );
344
345   if ( eglChooseConfig( mEglDisplay, &(configAttribs[0]), &mEglConfig, 1, &numConfigs ) != EGL_TRUE )
346   {
347     EGLint error = eglGetError();
348     switch (error)
349     {
350       case EGL_BAD_DISPLAY:
351       {
352         DALI_LOG_ERROR("Display is not an EGL display connection\n");
353         break;
354       }
355       case EGL_BAD_ATTRIBUTE:
356       {
357         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");
358         break;
359       }
360       case EGL_NOT_INITIALIZED:
361       {
362         DALI_LOG_ERROR("Display has not been initialized\n");
363         break;
364       }
365       case EGL_BAD_PARAMETER:
366       {
367         DALI_LOG_ERROR("The parameter numConfig is NULL\n");
368         break;
369       }
370       default:
371       {
372         DALI_LOG_ERROR("Unknown error.\n");
373       }
374     }
375     DALI_ASSERT_ALWAYS(false && "eglChooseConfig failed!");
376   }
377
378   if ( numConfigs != 1 )
379   {
380     DALI_LOG_ERROR("No configurations found.\n");
381
382     TEST_EGL_ERROR("eglChooseConfig");
383   }
384 }
385
386 void EglImplementation::CreateSurfaceWindow( EGLNativeWindowType window, ColorDepth depth )
387 {
388   DALI_ASSERT_ALWAYS( ( mCurrentEglSurface == 0 ) && "EGL surface already exists" );
389
390   mEglNativeWindow = window;
391   mColorDepth = depth;
392   mIsWindow = true;
393
394   // egl choose config
395   ChooseConfig(mIsWindow, mColorDepth);
396
397   mCurrentEglSurface = eglCreateWindowSurface( mEglDisplay, mEglConfig, mEglNativeWindow, NULL );
398   TEST_EGL_ERROR("eglCreateWindowSurface");
399
400   DALI_ASSERT_ALWAYS( mCurrentEglSurface && "Create window surface failed" );
401 }
402
403 EGLSurface EglImplementation::CreateSurfacePixmap( EGLNativePixmapType pixmap, ColorDepth depth )
404 {
405   mCurrentEglNativePixmap = pixmap;
406   mColorDepth = depth;
407   mIsWindow = false;
408
409   // egl choose config
410   ChooseConfig(mIsWindow, mColorDepth);
411
412   mCurrentEglSurface = eglCreatePixmapSurface( mEglDisplay, mEglConfig, mCurrentEglNativePixmap, NULL );
413   TEST_EGL_ERROR("eglCreatePixmapSurface");
414
415   DALI_ASSERT_ALWAYS( mCurrentEglSurface && "Create pixmap surface failed" );
416
417   return mCurrentEglSurface;
418 }
419
420 bool EglImplementation::ReplaceSurfaceWindow( EGLNativeWindowType window )
421 {
422   bool contextLost = false;
423
424   // display connection has not changed, then we can just create a new surface
425   //  the surface is bound to the context, so set the context to null
426   MakeContextNull();
427
428   // destroy the surface
429   DestroySurface();
430
431   // create the EGL surface
432   CreateSurfaceWindow( window, mColorDepth );
433
434   // set the context to be current with the new surface
435   MakeContextCurrent();
436
437   return contextLost;
438 }
439
440 bool EglImplementation::ReplaceSurfacePixmap( EGLNativePixmapType pixmap, EGLSurface& eglSurface )
441 {
442   bool contextLost = false;
443
444   // display connection has not changed, then we can just create a new surface
445   // create the EGL surface
446   eglSurface = CreateSurfacePixmap( pixmap, mColorDepth );
447
448   // set the eglSurface to be current
449   MakeCurrent( pixmap, eglSurface );
450
451   return contextLost;
452 }
453
454 EGLDisplay EglImplementation::GetDisplay() const
455 {
456   return mEglDisplay;
457 }
458
459 EGLDisplay EglImplementation::GetContext() const
460 {
461   return mEglContext;
462 }
463
464 } // namespace Adaptor
465
466 } // namespace Internal
467
468 } // namespace Dali
469
470 #pragma GCC diagnostic pop