7ca4366226921abaa0e8adb84fbaec7e3d84bf95
[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( 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     // Make context null to prevent crash in driver side
159     MakeContextNull();
160     eglDestroySurface( mEglDisplay, mCurrentEglSurface );
161     mCurrentEglSurface = 0;
162   }
163 }
164
165 void EglImplementation::MakeContextCurrent()
166 {
167   mContextCurrent = true;
168
169   if(mIsOwnSurface)
170   {
171     eglMakeCurrent( mEglDisplay, mCurrentEglSurface, mCurrentEglSurface, mEglContext );
172   }
173
174   EGLint error = eglGetError();
175
176   if ( error != EGL_SUCCESS )
177   {
178     Egl::PrintError(error);
179
180     DALI_ASSERT_ALWAYS(false && "MakeContextCurrent failed!");
181   }
182
183   // We want to display this information all the time, so use the LogMessage directly
184   Integration::Log::LogMessage(Integration::Log::DebugInfo, "EGL Information\n"
185       "            Vendor:        %s\n"
186       "            Version:       %s\n"
187       "            Client APIs:   %s\n"
188       "            Extensions:    %s\n",
189       eglQueryString(mEglDisplay, EGL_VENDOR),
190       eglQueryString(mEglDisplay, EGL_VERSION),
191       eglQueryString(mEglDisplay, EGL_CLIENT_APIS),
192       eglQueryString(mEglDisplay, EGL_EXTENSIONS));
193 }
194
195 void EglImplementation::MakeCurrent( EGLNativePixmapType pixmap, EGLSurface eglSurface )
196 {
197   mCurrentEglNativePixmap = pixmap;
198   mCurrentEglSurface = eglSurface;
199
200   if(mIsOwnSurface)
201   {
202     eglMakeCurrent( mEglDisplay, mCurrentEglSurface, mCurrentEglSurface, mEglContext );
203   }
204
205   EGLint error = eglGetError();
206
207   if ( error != EGL_SUCCESS )
208   {
209     Egl::PrintError(error);
210
211     DALI_ASSERT_ALWAYS(false && "MakeCurrent failed!");
212   }
213 }
214
215 void EglImplementation::MakeContextNull()
216 {
217   mContextCurrent = false;
218   // clear the current context
219   eglMakeCurrent( mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
220 }
221
222 void EglImplementation::TerminateGles()
223 {
224   if ( mGlesInitialized )
225   {
226     // Make context null to prevent crash in driver side
227     MakeContextNull();
228
229     if(mIsOwnSurface && mCurrentEglSurface)
230     {
231       eglDestroySurface(mEglDisplay, mCurrentEglSurface);
232     }
233     eglDestroyContext(mEglDisplay, mEglContext);
234
235     eglTerminate(mEglDisplay);
236
237     mEglDisplay = NULL;
238     mEglConfig  = NULL;
239     mEglContext = NULL;
240     mCurrentEglSurface = NULL;
241
242     mGlesInitialized = false;
243   }
244 }
245
246 bool EglImplementation::IsGlesInitialized() const
247 {
248   return mGlesInitialized;
249 }
250
251 void EglImplementation::SwapBuffers()
252 {
253   eglSwapBuffers( mEglDisplay, mCurrentEglSurface );
254 }
255
256 void EglImplementation::CopyBuffers()
257 {
258   eglCopyBuffers( mEglDisplay, mCurrentEglSurface, mCurrentEglNativePixmap );
259 }
260
261 void EglImplementation::WaitGL()
262 {
263   eglWaitGL();
264 }
265
266 void EglImplementation::ChooseConfig( bool isWindowType, ColorDepth depth )
267 {
268   if(mEglConfig && isWindowType == mIsWindow && mColorDepth == depth)
269   {
270     return;
271   }
272
273   mIsWindow = isWindowType;
274
275   EGLint numConfigs;
276   Vector<EGLint> configAttribs;
277   configAttribs.Reserve(31);
278
279   if(isWindowType)
280   {
281     configAttribs.PushBack( EGL_SURFACE_TYPE );
282     configAttribs.PushBack( EGL_WINDOW_BIT );
283   }
284   else
285   {
286     configAttribs.PushBack( EGL_SURFACE_TYPE );
287     configAttribs.PushBack( EGL_PIXMAP_BIT );
288   }
289
290   configAttribs.PushBack( EGL_RENDERABLE_TYPE );
291
292 #if DALI_GLES_VERSION >= 30
293
294 #ifdef _ARCH_ARM_
295   configAttribs.PushBack( EGL_OPENGL_ES3_BIT_KHR );
296 #else
297   // There is a bug in the desktop emulator
298   // Requesting for ES3 causes eglCreateContext even though it allows to ask
299   // for a configuration that supports GLES 3.0
300   configAttribs.PushBack( EGL_OPENGL_ES2_BIT );
301 #endif // _ARCH_ARM_
302
303 #else // DALI_GLES_VERSION >= 30
304
305   Integration::Log::LogMessage( Integration::Log::DebugInfo, "Using OpenGL ES 2 \n" );
306   configAttribs.PushBack( EGL_OPENGL_ES2_BIT );
307
308 #endif //DALI_GLES_VERSION >= 30
309
310 #if DALI_GLES_VERSION >= 30
311 // TODO: enable this flag when it becomes supported
312 //  configAttribs.PushBack( EGL_CONTEXT_FLAGS_KHR );
313 //  configAttribs.PushBack( EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR );
314 #endif //DALI_GLES_VERSION >= 30
315
316   configAttribs.PushBack( EGL_RED_SIZE );
317   configAttribs.PushBack( 8 );
318   configAttribs.PushBack( EGL_GREEN_SIZE );
319   configAttribs.PushBack( 8 );
320   configAttribs.PushBack( EGL_BLUE_SIZE );
321   configAttribs.PushBack( 8 );
322
323   configAttribs.PushBack( EGL_ALPHA_SIZE );
324 #ifdef _ARCH_ARM_
325   configAttribs.PushBack( (depth == COLOR_DEPTH_32) ? 8 : 0 );
326 #else
327   // There is a bug in the desktop emulator
328   // setting EGL_ALPHA_SIZE to 8 results in eglChooseConfig failing
329   configAttribs.PushBack( 0 );
330 #endif // _ARCH_ARM_
331
332   configAttribs.PushBack( EGL_DEPTH_SIZE );
333   configAttribs.PushBack( 24 );
334   configAttribs.PushBack( EGL_STENCIL_SIZE );
335   configAttribs.PushBack( 8 );
336 #ifndef DALI_PROFILE_UBUNTU
337   if( mMultiSamplingLevel != EGL_DONT_CARE )
338   {
339     configAttribs.PushBack( EGL_SAMPLES );
340     configAttribs.PushBack( mMultiSamplingLevel );
341     configAttribs.PushBack( EGL_SAMPLE_BUFFERS );
342     configAttribs.PushBack( 1 );
343   }
344 #endif // DALI_PROFILE_UBUNTU
345   configAttribs.PushBack( EGL_NONE );
346
347   if ( eglChooseConfig( mEglDisplay, &(configAttribs[0]), &mEglConfig, 1, &numConfigs ) != EGL_TRUE )
348   {
349     EGLint error = eglGetError();
350     switch (error)
351     {
352       case EGL_BAD_DISPLAY:
353       {
354         DALI_LOG_ERROR("Display is not an EGL display connection\n");
355         break;
356       }
357       case EGL_BAD_ATTRIBUTE:
358       {
359         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");
360         break;
361       }
362       case EGL_NOT_INITIALIZED:
363       {
364         DALI_LOG_ERROR("Display has not been initialized\n");
365         break;
366       }
367       case EGL_BAD_PARAMETER:
368       {
369         DALI_LOG_ERROR("The parameter numConfig is NULL\n");
370         break;
371       }
372       default:
373       {
374         DALI_LOG_ERROR("Unknown error.\n");
375       }
376     }
377     DALI_ASSERT_ALWAYS(false && "eglChooseConfig failed!");
378   }
379
380   if ( numConfigs != 1 )
381   {
382     DALI_LOG_ERROR("No configurations found.\n");
383
384     TEST_EGL_ERROR("eglChooseConfig");
385   }
386 }
387
388 void EglImplementation::CreateSurfaceWindow( EGLNativeWindowType window, ColorDepth depth )
389 {
390   DALI_ASSERT_ALWAYS( ( mCurrentEglSurface == 0 ) && "EGL surface already exists" );
391
392   mEglNativeWindow = window;
393   mColorDepth = depth;
394   mIsWindow = true;
395
396   // egl choose config
397   ChooseConfig(mIsWindow, mColorDepth);
398
399   mCurrentEglSurface = eglCreateWindowSurface( mEglDisplay, mEglConfig, mEglNativeWindow, NULL );
400   TEST_EGL_ERROR("eglCreateWindowSurface");
401
402   DALI_ASSERT_ALWAYS( mCurrentEglSurface && "Create window surface failed" );
403 }
404
405 EGLSurface EglImplementation::CreateSurfacePixmap( EGLNativePixmapType pixmap, ColorDepth depth )
406 {
407   mCurrentEglNativePixmap = pixmap;
408   mColorDepth = depth;
409   mIsWindow = false;
410
411   // egl choose config
412   ChooseConfig(mIsWindow, mColorDepth);
413
414   mCurrentEglSurface = eglCreatePixmapSurface( mEglDisplay, mEglConfig, mCurrentEglNativePixmap, NULL );
415   TEST_EGL_ERROR("eglCreatePixmapSurface");
416
417   DALI_ASSERT_ALWAYS( mCurrentEglSurface && "Create pixmap surface failed" );
418
419   return mCurrentEglSurface;
420 }
421
422 bool EglImplementation::ReplaceSurfaceWindow( EGLNativeWindowType window )
423 {
424   bool contextLost = false;
425
426   // display connection has not changed, then we can just create a new surface
427   //  the surface is bound to the context, so set the context to null
428   MakeContextNull();
429
430   // destroy the surface
431   DestroySurface();
432
433   // create the EGL surface
434   CreateSurfaceWindow( window, mColorDepth );
435
436   // set the context to be current with the new surface
437   MakeContextCurrent();
438
439   return contextLost;
440 }
441
442 bool EglImplementation::ReplaceSurfacePixmap( EGLNativePixmapType pixmap, EGLSurface& eglSurface )
443 {
444   bool contextLost = false;
445
446   // display connection has not changed, then we can just create a new surface
447   // create the EGL surface
448   eglSurface = CreateSurfacePixmap( pixmap, mColorDepth );
449
450   // set the eglSurface to be current
451   MakeCurrent( pixmap, eglSurface );
452
453   return contextLost;
454 }
455
456 EGLDisplay EglImplementation::GetDisplay() const
457 {
458   return mEglDisplay;
459 }
460
461 EGLDisplay EglImplementation::GetContext() const
462 {
463   return mEglContext;
464 }
465
466 } // namespace Adaptor
467
468 } // namespace Internal
469
470 } // namespace Dali
471
472 #pragma GCC diagnostic pop