1 // Copyright (C) 2013 Patryk Nadrowski
\r
2 // This file is part of the "Irrlicht Engine".
\r
3 // For conditions of distribution and use, see copyright notice in Irrlicht.h
\r
5 #include "CEGLManager.h"
\r
7 #ifdef _IRR_COMPILE_WITH_EGL_MANAGER_
\r
9 #include "irrString.h"
\r
10 #include "irrArray.h"
\r
14 #if defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_)
\r
15 #include <android/native_activity.h>
\r
23 CEGLManager::CEGLManager() : IContextManager(), EglWindow(0), EglDisplay(EGL_NO_DISPLAY),
\r
24 EglSurface(EGL_NO_SURFACE), EglContext(EGL_NO_CONTEXT), EglConfig(0), MajorVersion(0), MinorVersion(0), libHandle(NULL)
\r
27 setDebugName("CEGLManager");
\r
31 CEGLManager::~CEGLManager()
\r
38 bool CEGLManager::initialize(const SIrrlichtCreationParameters& params, const SExposedVideoData& data)
\r
44 if (EglWindow != 0 && EglDisplay != EGL_NO_DISPLAY)
\r
47 // Window is depend on platform.
\r
48 #if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
\r
49 EglWindow = (NativeWindowType)Data.OpenGLWin32.HWnd;
\r
50 Data.OpenGLWin32.HDc = GetDC((HWND)EglWindow);
\r
51 EglDisplay = eglGetDisplay((NativeDisplayType)Data.OpenGLWin32.HDc);
\r
52 #elif defined(_IRR_EMSCRIPTEN_PLATFORM_)
\r
54 EglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
\r
55 #elif defined(_IRR_COMPILE_WITH_X11_DEVICE_)
\r
56 EglWindow = (NativeWindowType)Data.OpenGLLinux.X11Window;
\r
57 EglDisplay = eglGetDisplay((NativeDisplayType)Data.OpenGLLinux.X11Display);
\r
58 #elif defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_)
\r
59 EglWindow = (ANativeWindow*)Data.OGLESAndroid.Window;
\r
60 EglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
\r
61 #elif defined(_IRR_COMPILE_WITH_FB_DEVICE_)
\r
62 EglWindow = (NativeWindowType)Data.OpenGLFB.Window;
\r
63 EglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
\r
66 // We must check if EGL display is valid.
\r
67 if (EglDisplay == EGL_NO_DISPLAY)
\r
69 os::Printer::log("Could not get EGL display.");
\r
74 // Initialize EGL here.
\r
75 if (!eglInitialize(EglDisplay, &MajorVersion, &MinorVersion))
\r
77 os::Printer::log("Could not initialize EGL display.");
\r
79 EglDisplay = EGL_NO_DISPLAY;
\r
84 os::Printer::log("EGL version", core::stringc(MajorVersion+(MinorVersion*0.1f)).c_str());
\r
89 void CEGLManager::terminate()
\r
91 if (EglWindow == 0 && EglDisplay == EGL_NO_DISPLAY)
\r
94 if (EglDisplay != EGL_NO_DISPLAY)
\r
96 // We should unbind current EGL context before terminate EGL.
\r
97 eglMakeCurrent(EglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
\r
99 eglTerminate(EglDisplay);
\r
100 EglDisplay = EGL_NO_DISPLAY;
\r
103 #if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
\r
104 if (Data.OpenGLWin32.HDc)
\r
106 ReleaseDC((HWND)EglWindow, (HDC)Data.OpenGLWin32.HDc);
\r
107 Data.OpenGLWin32.HDc = 0;
\r
115 dlclose(libHandle);
\r
118 bool CEGLManager::generateSurface()
\r
120 if (EglDisplay == EGL_NO_DISPLAY)
\r
123 if (EglSurface != EGL_NO_SURFACE)
\r
126 // We should assign new WindowID on platforms, where WindowID may change at runtime,
\r
127 // at this time only Android support this feature.
\r
128 // this needs an update method instead!
\r
130 #if defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_)
\r
131 EglWindow = (ANativeWindow*)Data.OGLESAndroid.Window;
\r
134 #if defined(_IRR_EMSCRIPTEN_PLATFORM_)
\r
135 // eglChooseConfig is currently only implemented as stub in emscripten (version 1.37.22 at point of writing)
\r
136 // But the other solution would also be fine as it also only generates a single context so there is not much to choose from.
\r
137 EglConfig = chooseConfig(ECS_IRR_CHOOSE);
\r
139 EglConfig = chooseConfig(ECS_EGL_CHOOSE_FIRST_LOWER_EXPECTATIONS);
\r
142 if ( EglConfig == 0 )
\r
144 os::Printer::log("Could not get config for EGL display.");
\r
149 #if defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_)
\r
151 eglGetConfigAttrib(EglDisplay, EglConfig, EGL_NATIVE_VISUAL_ID, &Format);
\r
153 ANativeWindow_setBuffersGeometry(EglWindow, 0, 0, Format);
\r
156 // Now we are able to create EGL surface.
\r
157 EglSurface = eglCreateWindowSurface(EglDisplay, EglConfig, EglWindow, 0);
\r
159 if (EGL_NO_SURFACE == EglSurface)
\r
160 EglSurface = eglCreateWindowSurface(EglDisplay, EglConfig, 0, 0);
\r
162 if (EGL_NO_SURFACE == EglSurface)
\r
163 os::Printer::log("Could not create EGL surface.");
\r
165 #ifdef EGL_VERSION_1_2
\r
166 if (MinorVersion > 1)
\r
167 eglBindAPI(EGL_OPENGL_ES_API);
\r
171 eglSwapInterval(EglDisplay, 1);
\r
176 EGLConfig CEGLManager::chooseConfig(EConfigStyle confStyle)
\r
178 EGLConfig configResult = 0;
\r
180 // Find proper OpenGL BIT.
\r
181 EGLint eglOpenGLBIT = 0;
\r
182 switch (Params.DriverType)
\r
185 eglOpenGLBIT = EGL_OPENGL_ES_BIT;
\r
189 eglOpenGLBIT = EGL_OPENGL_ES2_BIT;
\r
195 if ( confStyle == ECS_EGL_CHOOSE_FIRST_LOWER_EXPECTATIONS )
\r
202 EGL_ALPHA_SIZE, Params.WithAlphaChannel ? 1:0,
\r
203 EGL_BUFFER_SIZE, Params.Bits,
\r
204 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
\r
205 EGL_DEPTH_SIZE, Params.ZBufferBits,
\r
206 EGL_STENCIL_SIZE, Params.Stencilbuffer,
\r
207 EGL_SAMPLE_BUFFERS, Params.AntiAlias ? 1:0,
\r
208 EGL_SAMPLES, Params.AntiAlias,
\r
209 #ifdef EGL_VERSION_1_3
\r
210 EGL_RENDERABLE_TYPE, eglOpenGLBIT,
\r
215 EGLint numConfigs = 0;
\r
218 // Choose the best EGL config.
\r
219 // TODO: We should also have a confStyle ECS_EGL_CHOOSE_CLOSEST
\r
220 // which doesn't take first result of eglChooseConfigs,
\r
221 // but the closest to requested parameters. eglChooseConfigs
\r
222 // can return more than 1 result and first one might have
\r
223 // "better" values than requested (more bits per pixel etc).
\r
224 // So this returns the config which can do most, not the
\r
225 // config which is closest to the requested parameters.
\r
227 while (!eglChooseConfig(EglDisplay, Attribs, &configResult, 1, &numConfigs) || !numConfigs)
\r
232 if (Attribs[19] > 2) // Params.AntiAlias
\r
236 Attribs[17] = 0; // Params.Stencilbuffer
\r
237 Attribs[19] = 0; // Params.AntiAlias
\r
242 if (Attribs[7]) // Params.WithAlphaChannel
\r
246 if (Params.AntiAlias)
\r
249 Attribs[19] = Params.AntiAlias;
\r
257 if (Attribs[15]) // Params.Stencilbuffer
\r
261 if (Params.AntiAlias)
\r
264 Attribs[19] = Params.AntiAlias;
\r
271 case 2: // depth size
\r
272 if (Attribs[13] > 16) // Params.ZBufferBits
\r
279 case 1: // buffer size
\r
280 if (Attribs[9] > 16) // Params.Bits
\r
292 if (Params.AntiAlias && !Attribs[17])
\r
293 os::Printer::log("No multisampling.");
\r
295 if (Params.WithAlphaChannel && !Attribs[7])
\r
296 os::Printer::log("No alpha.");
\r
298 if (Params.Stencilbuffer && !Attribs[15])
\r
299 os::Printer::log("No stencil buffer.");
\r
301 if (Params.ZBufferBits > Attribs[13])
\r
302 os::Printer::log("No full depth buffer.");
\r
304 if (Params.Bits > Attribs[9])
\r
305 os::Printer::log("No full color buffer.");
\r
307 else if ( confStyle == ECS_IRR_CHOOSE )
\r
309 // find number of available configs
\r
311 if ( eglGetConfigs( EglDisplay, NULL, 0, &numConfigs) == EGL_FALSE )
\r
317 if ( numConfigs <= 0 )
\r
320 // Get all available configs.
\r
321 EGLConfig * configs = new EGLConfig[numConfigs];
\r
322 if ( eglGetConfigs( EglDisplay, configs, numConfigs, &numConfigs) == EGL_FALSE )
\r
328 // Find the best one.
\r
329 core::array<SConfigRating> ratings((u32)numConfigs);
\r
330 for ( u32 i=0; i < (u32)numConfigs; ++i )
\r
333 r.config = configs[i];
\r
334 r.rating = rateConfig(r.config, eglOpenGLBIT);
\r
336 if ( r.rating >= 0 )
\r
337 ratings.push_back(r);
\r
340 if ( ratings.size() > 0 )
\r
343 configResult = ratings[0].config;
\r
345 if ( ratings[0].rating != 0 )
\r
347 // This is just to print some log info (it also rates again while doing that, but rating is cheap enough, so that doesn't matter here).
\r
348 rateConfig(ratings[0].config, eglOpenGLBIT, true);
\r
355 return configResult;
\r
358 irr::s32 CEGLManager::rateConfig(EGLConfig config, EGLint eglOpenGLBIT, bool log)
\r
360 // some values must be there or we ignore the config
\r
361 #ifdef EGL_VERSION_1_3
\r
362 EGLint attribRenderableType = 0;
\r
363 eglGetConfigAttrib( EglDisplay, config, EGL_RENDERABLE_TYPE, &attribRenderableType);
\r
364 if ( attribRenderableType != eglOpenGLBIT )
\r
367 os::Printer::log("EGL_RENDERABLE_TYPE != eglOpenGLBIT");
\r
371 EGLint attribSurfaceType = 0;
\r
372 eglGetConfigAttrib( EglDisplay, config, EGL_SURFACE_TYPE, &attribSurfaceType);
\r
373 if ( attribSurfaceType != EGL_WINDOW_BIT )
\r
376 os::Printer::log("EGL_SURFACE_TYPE!= EGL_WINDOW_BIT");
\r
380 // Generally we give a really bad rating if attributes are worse than requested
\r
381 // We give a slight worse rating if attributes are not exact as requested
\r
382 // And we use some priorities which might make sense (but not really fine-tuned,
\r
383 // so if you think other priorities would be better don't worry about changing the values.
\r
386 EGLint attribBufferSize = 0;
\r
387 eglGetConfigAttrib( EglDisplay, config, EGL_BUFFER_SIZE, &attribBufferSize);
\r
388 if ( attribBufferSize < Params.Bits )
\r
391 os::Printer::log("No full color buffer.");
\r
394 if ( attribBufferSize > Params.Bits )
\r
397 os::Printer::log("Larger color buffer.", ELL_DEBUG);
\r
401 EGLint attribRedSize = 0;
\r
402 eglGetConfigAttrib( EglDisplay, config, EGL_RED_SIZE, &attribRedSize);
\r
403 if ( attribRedSize < 5 && Params.Bits >= 4 )
\r
405 else if ( attribRedSize < 8 && Params.Bits >= 24)
\r
407 else if ( attribRedSize >= 8 && Params.Bits < 24 )
\r
409 EGLint attribGreenSize = 0;
\r
410 eglGetConfigAttrib( EglDisplay, config, EGL_GREEN_SIZE, &attribGreenSize);
\r
411 if ( attribGreenSize < 5 && Params.Bits >= 4 )
\r
413 else if ( attribGreenSize < 8 && Params.Bits >= 24)
\r
415 else if ( attribGreenSize >= 8 && Params.Bits < 24 )
\r
417 EGLint attribBlueSize = 0;
\r
418 eglGetConfigAttrib( EglDisplay, config, EGL_BLUE_SIZE, &attribBlueSize);
\r
419 if ( attribBlueSize < 5 && Params.Bits >= 4 )
\r
421 else if ( attribBlueSize < 8 && Params.Bits >= 24)
\r
423 else if ( attribBlueSize >= 8 && Params.Bits < 24 )
\r
426 EGLint attribAlphaSize = 0;
\r
427 eglGetConfigAttrib( EglDisplay, config, EGL_ALPHA_SIZE, &attribAlphaSize);
\r
428 if ( Params.WithAlphaChannel && attribAlphaSize == 0 )
\r
431 os::Printer::log("No alpha.");
\r
434 else if ( !Params.WithAlphaChannel && attribAlphaSize > 0 )
\r
437 os::Printer::log("Got alpha (unrequested).", ELL_DEBUG);
\r
441 EGLint attribStencilSize = 0;
\r
442 eglGetConfigAttrib( EglDisplay, config, EGL_STENCIL_SIZE, &attribStencilSize);
\r
443 if ( Params.Stencilbuffer && attribStencilSize == 0 )
\r
446 os::Printer::log("No stencil buffer.");
\r
449 else if ( !Params.Stencilbuffer && attribStencilSize > 0 )
\r
452 os::Printer::log("Got a stencil buffer (unrequested).", ELL_DEBUG);
\r
456 EGLint attribDepthSize = 0;
\r
457 eglGetConfigAttrib( EglDisplay, config, EGL_DEPTH_SIZE, &attribDepthSize);
\r
458 if ( attribDepthSize < Params.ZBufferBits )
\r
462 if (attribDepthSize > 0)
\r
463 os::Printer::log("No full depth buffer.");
\r
465 os::Printer::log("No depth buffer.");
\r
469 else if ( attribDepthSize != Params.ZBufferBits )
\r
473 if ( Params.ZBufferBits == 0 )
\r
474 os::Printer::log("Got a depth buffer (unrequested).", ELL_DEBUG);
\r
476 os::Printer::log("Got a larger depth buffer.", ELL_DEBUG);
\r
481 EGLint attribSampleBuffers=0, attribSamples = 0;
\r
482 eglGetConfigAttrib( EglDisplay, config, EGL_SAMPLE_BUFFERS, &attribSampleBuffers);
\r
483 eglGetConfigAttrib( EglDisplay, config, EGL_SAMPLES, &attribSamples);
\r
484 if ( Params.AntiAlias && attribSampleBuffers == 0 )
\r
487 os::Printer::log("No multisampling.");
\r
490 else if ( Params.AntiAlias && attribSampleBuffers && attribSamples < Params.AntiAlias )
\r
493 os::Printer::log("Multisampling with less samples than requested.", ELL_DEBUG);
\r
496 else if ( Params.AntiAlias && attribSampleBuffers && attribSamples > Params.AntiAlias )
\r
499 os::Printer::log("Multisampling with more samples than requested.", ELL_DEBUG);
\r
502 else if ( !Params.AntiAlias && attribSampleBuffers > 0 )
\r
505 os::Printer::log("Got multisampling (unrequested).", ELL_DEBUG);
\r
512 void CEGLManager::destroySurface()
\r
514 if (EglSurface == EGL_NO_SURFACE)
\r
517 // We should unbind current EGL context before destroy EGL surface.
\r
518 eglMakeCurrent(EglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
\r
520 eglDestroySurface(EglDisplay, EglSurface);
\r
521 EglSurface = EGL_NO_SURFACE;
\r
524 bool CEGLManager::generateContext()
\r
526 if (EglDisplay == EGL_NO_DISPLAY || EglSurface == EGL_NO_SURFACE)
\r
529 if (EglContext != EGL_NO_CONTEXT)
\r
532 EGLint OpenGLESVersion = 0;
\r
534 switch (Params.DriverType)
\r
537 OpenGLESVersion = 1;
\r
541 OpenGLESVersion = 2;
\r
547 EGLint ContextAttrib[] =
\r
549 #ifdef EGL_VERSION_1_3
\r
550 EGL_CONTEXT_CLIENT_VERSION, OpenGLESVersion,
\r
555 EglContext = eglCreateContext(EglDisplay, EglConfig, EGL_NO_CONTEXT, ContextAttrib);
\r
557 if (testEGLError())
\r
559 os::Printer::log("Could not create EGL context.", ELL_ERROR);
\r
563 os::Printer::log("EGL context created with OpenGLESVersion: ", core::stringc((int)OpenGLESVersion), ELL_DEBUG);
\r
568 void CEGLManager::destroyContext()
\r
570 if (EglContext == EGL_NO_CONTEXT)
\r
573 // We must unbind current EGL context before destroy it.
\r
574 eglMakeCurrent(EglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
\r
575 eglDestroyContext(EglDisplay, EglContext);
\r
577 EglContext = EGL_NO_CONTEXT;
\r
580 bool CEGLManager::activateContext(const SExposedVideoData& videoData, bool restorePrimaryOnZero)
\r
582 eglMakeCurrent(EglDisplay, EglSurface, EglSurface, EglContext);
\r
584 if (testEGLError())
\r
586 os::Printer::log("Could not make EGL context current.");
\r
592 const SExposedVideoData& CEGLManager::getContext() const
\r
597 void* CEGLManager::getProcAddress(const std::string &procName)
\r
600 proc = (void*)eglGetProcAddress(procName.c_str());
\r
601 if (!proc) { // fallback
\r
603 libHandle = dlopen("libGLESv2.so", RTLD_LAZY);
\r
605 proc = dlsym(libHandle, procName.c_str());
\r
610 bool CEGLManager::swapBuffers()
\r
612 return (eglSwapBuffers(EglDisplay, EglSurface)==EGL_TRUE);
\r
615 bool CEGLManager::testEGLError()
\r
617 #if defined(EGL_VERSION_1_0) && defined(_DEBUG)
\r
618 EGLint status = eglGetError();
\r
624 case EGL_NOT_INITIALIZED :
\r
625 os::Printer::log("Not Initialized", ELL_ERROR);
\r
627 case EGL_BAD_ACCESS:
\r
628 os::Printer::log("Bad Access", ELL_ERROR);
\r
630 case EGL_BAD_ALLOC:
\r
631 os::Printer::log("Bad Alloc", ELL_ERROR);
\r
633 case EGL_BAD_ATTRIBUTE:
\r
634 os::Printer::log("Bad Attribute", ELL_ERROR);
\r
636 case EGL_BAD_CONTEXT:
\r
637 os::Printer::log("Bad Context", ELL_ERROR);
\r
639 case EGL_BAD_CONFIG:
\r
640 os::Printer::log("Bad Config", ELL_ERROR);
\r
642 case EGL_BAD_CURRENT_SURFACE:
\r
643 os::Printer::log("Bad Current Surface", ELL_ERROR);
\r
645 case EGL_BAD_DISPLAY:
\r
646 os::Printer::log("Bad Display", ELL_ERROR);
\r
648 case EGL_BAD_SURFACE:
\r
649 os::Printer::log("Bad Surface", ELL_ERROR);
\r
651 case EGL_BAD_MATCH:
\r
652 os::Printer::log("Bad Match", ELL_ERROR);
\r
654 case EGL_BAD_PARAMETER:
\r
655 os::Printer::log("Bad Parameter", ELL_ERROR);
\r
657 case EGL_BAD_NATIVE_PIXMAP:
\r
658 os::Printer::log("Bad Native Pixmap", ELL_ERROR);
\r
660 case EGL_BAD_NATIVE_WINDOW:
\r
661 os::Printer::log("Bad Native Window", ELL_ERROR);
\r
663 case EGL_CONTEXT_LOST:
\r
664 os::Printer::log("Context Lost", ELL_ERROR);
\r