irrlicht/source/Irrlicht/CEGLManager.cpp

680 lines
17 KiB
C++
Executable File

// Copyright (C) 2013 Patryk Nadrowski
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in Irrlicht.h
#include "CEGLManager.h"
#ifdef _IRR_COMPILE_WITH_EGL_MANAGER_
#include "irrString.h"
#include "irrArray.h"
#include "os.h"
#include <dlfcn.h>
#if defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_)
#include <android/native_activity.h>
#endif
namespace irr
{
namespace video
{
CEGLManager::CEGLManager() : IContextManager(), EglWindow(0), EglDisplay(EGL_NO_DISPLAY),
EglSurface(EGL_NO_SURFACE), EglContext(EGL_NO_CONTEXT), EglConfig(0), MajorVersion(0), MinorVersion(0), libHandle(NULL)
{
#ifdef _DEBUG
setDebugName("CEGLManager");
#endif
}
CEGLManager::~CEGLManager()
{
destroyContext();
destroySurface();
terminate();
}
bool CEGLManager::initialize(const SIrrlichtCreationParameters& params, const SExposedVideoData& data)
{
// store new data
Params=params;
Data=data;
if (EglWindow != 0 && EglDisplay != EGL_NO_DISPLAY)
return true;
// Window is depend on platform.
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
EglWindow = (NativeWindowType)Data.OpenGLWin32.HWnd;
Data.OpenGLWin32.HDc = GetDC((HWND)EglWindow);
EglDisplay = eglGetDisplay((NativeDisplayType)Data.OpenGLWin32.HDc);
#elif defined(_IRR_EMSCRIPTEN_PLATFORM_)
EglWindow = 0;
EglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
#elif defined(_IRR_COMPILE_WITH_X11_DEVICE_)
EglWindow = (NativeWindowType)Data.OpenGLLinux.X11Window;
EglDisplay = eglGetDisplay((NativeDisplayType)Data.OpenGLLinux.X11Display);
#elif defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_)
EglWindow = (ANativeWindow*)Data.OGLESAndroid.Window;
EglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
#elif defined(_IRR_COMPILE_WITH_FB_DEVICE_)
EglWindow = (NativeWindowType)Data.OpenGLFB.Window;
EglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
#endif
// We must check if EGL display is valid.
if (EglDisplay == EGL_NO_DISPLAY)
{
os::Printer::log("Could not get EGL display.");
terminate();
return false;
}
// Initialize EGL here.
if (!eglInitialize(EglDisplay, &MajorVersion, &MinorVersion))
{
os::Printer::log("Could not initialize EGL display.");
EglDisplay = EGL_NO_DISPLAY;
terminate();
return false;
}
else
os::Printer::log("EGL version", core::stringc(MajorVersion+(MinorVersion*0.1f)).c_str());
return true;
}
void CEGLManager::terminate()
{
if (EglWindow == 0 && EglDisplay == EGL_NO_DISPLAY)
return;
if (EglDisplay != EGL_NO_DISPLAY)
{
// We should unbind current EGL context before terminate EGL.
eglMakeCurrent(EglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglTerminate(EglDisplay);
EglDisplay = EGL_NO_DISPLAY;
}
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
if (Data.OpenGLWin32.HDc)
{
ReleaseDC((HWND)EglWindow, (HDC)Data.OpenGLWin32.HDc);
Data.OpenGLWin32.HDc = 0;
}
#endif
MajorVersion = 0;
MinorVersion = 0;
if (libHandle)
dlclose(libHandle);
}
bool CEGLManager::generateSurface()
{
if (EglDisplay == EGL_NO_DISPLAY)
return false;
if (EglSurface != EGL_NO_SURFACE)
return true;
// We should assign new WindowID on platforms, where WindowID may change at runtime,
// at this time only Android support this feature.
// this needs an update method instead!
#if defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_)
EglWindow = (ANativeWindow*)Data.OGLESAndroid.Window;
#endif
#if defined(_IRR_EMSCRIPTEN_PLATFORM_)
// eglChooseConfig is currently only implemented as stub in emscripten (version 1.37.22 at point of writing)
// But the other solution would also be fine as it also only generates a single context so there is not much to choose from.
EglConfig = chooseConfig(ECS_IRR_CHOOSE);
#else
EglConfig = chooseConfig(ECS_EGL_CHOOSE_FIRST_LOWER_EXPECTATIONS);
#endif
if ( EglConfig == 0 )
{
os::Printer::log("Could not get config for EGL display.");
return false;
}
#if defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_)
EGLint Format = 0;
eglGetConfigAttrib(EglDisplay, EglConfig, EGL_NATIVE_VISUAL_ID, &Format);
ANativeWindow_setBuffersGeometry(EglWindow, 0, 0, Format);
#endif
// Now we are able to create EGL surface.
EglSurface = eglCreateWindowSurface(EglDisplay, EglConfig, EglWindow, 0);
if (EGL_NO_SURFACE == EglSurface)
EglSurface = eglCreateWindowSurface(EglDisplay, EglConfig, 0, 0);
if (EGL_NO_SURFACE == EglSurface)
os::Printer::log("Could not create EGL surface.");
#ifdef EGL_VERSION_1_2
if (MinorVersion > 1)
eglBindAPI(EGL_OPENGL_ES_API);
#endif
if (Params.Vsync)
eglSwapInterval(EglDisplay, 1);
return true;
}
EGLConfig CEGLManager::chooseConfig(EConfigStyle confStyle)
{
EGLConfig configResult = 0;
// Find proper OpenGL BIT.
EGLint eglOpenGLBIT = 0;
switch (Params.DriverType)
{
case EDT_OGLES1:
eglOpenGLBIT = EGL_OPENGL_ES_BIT;
break;
case EDT_OGLES2:
case EDT_WEBGL1:
eglOpenGLBIT = EGL_OPENGL_ES2_BIT;
break;
default:
break;
}
if ( confStyle == ECS_EGL_CHOOSE_FIRST_LOWER_EXPECTATIONS )
{
EGLint Attribs[] =
{
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, Params.WithAlphaChannel ? 1:0,
EGL_BUFFER_SIZE, Params.Bits,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_DEPTH_SIZE, Params.ZBufferBits,
EGL_STENCIL_SIZE, Params.Stencilbuffer,
EGL_SAMPLE_BUFFERS, Params.AntiAlias ? 1:0,
EGL_SAMPLES, Params.AntiAlias,
#ifdef EGL_VERSION_1_3
EGL_RENDERABLE_TYPE, eglOpenGLBIT,
#endif
EGL_NONE, 0
};
EGLint numConfigs = 0;
u32 steps = 5;
// Choose the best EGL config.
// TODO: We should also have a confStyle ECS_EGL_CHOOSE_CLOSEST
// which doesn't take first result of eglChooseConfigs,
// but the closest to requested parameters. eglChooseConfigs
// can return more than 1 result and first one might have
// "better" values than requested (more bits per pixel etc).
// So this returns the config which can do most, not the
// config which is closest to the requested parameters.
//
while (!eglChooseConfig(EglDisplay, Attribs, &configResult, 1, &numConfigs) || !numConfigs)
{
switch (steps)
{
case 5: // samples
if (Attribs[19] > 2) // Params.AntiAlias
--Attribs[19];
else
{
Attribs[17] = 0; // Params.Stencilbuffer
Attribs[19] = 0; // Params.AntiAlias
--steps;
}
break;
case 4: // alpha
if (Attribs[7]) // Params.WithAlphaChannel
{
Attribs[7] = 0;
if (Params.AntiAlias)
{
Attribs[17] = 1;
Attribs[19] = Params.AntiAlias;
steps = 5;
}
}
else
--steps;
break;
case 3: // stencil
if (Attribs[15]) // Params.Stencilbuffer
{
Attribs[15] = 0;
if (Params.AntiAlias)
{
Attribs[17] = 1;
Attribs[19] = Params.AntiAlias;
steps = 5;
}
}
else
--steps;
break;
case 2: // depth size
if (Attribs[13] > 16) // Params.ZBufferBits
{
Attribs[13] -= 8;
}
else
--steps;
break;
case 1: // buffer size
if (Attribs[9] > 16) // Params.Bits
{
Attribs[9] -= 8;
}
else
--steps;
break;
default:
return 0;
}
}
if (Params.AntiAlias && !Attribs[17])
os::Printer::log("No multisampling.");
if (Params.WithAlphaChannel && !Attribs[7])
os::Printer::log("No alpha.");
if (Params.Stencilbuffer && !Attribs[15])
os::Printer::log("No stencil buffer.");
if (Params.ZBufferBits > Attribs[13])
os::Printer::log("No full depth buffer.");
if (Params.Bits > Attribs[9])
os::Printer::log("No full color buffer.");
}
else if ( confStyle == ECS_IRR_CHOOSE )
{
// find number of available configs
EGLint numConfigs;
if ( eglGetConfigs( EglDisplay, NULL, 0, &numConfigs) == EGL_FALSE )
{
testEGLError();
return 0;
}
if ( numConfigs <= 0 )
return 0;
// Get all available configs.
EGLConfig * configs = new EGLConfig[numConfigs];
if ( eglGetConfigs( EglDisplay, configs, numConfigs, &numConfigs) == EGL_FALSE )
{
testEGLError();
return 0;
}
// Find the best one.
core::array<SConfigRating> ratings((u32)numConfigs);
for ( u32 i=0; i < (u32)numConfigs; ++i )
{
SConfigRating r;
r.config = configs[i];
r.rating = rateConfig(r.config, eglOpenGLBIT);
if ( r.rating >= 0 )
ratings.push_back(r);
}
if ( ratings.size() > 0 )
{
ratings.sort();
configResult = ratings[0].config;
if ( ratings[0].rating != 0 )
{
// 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).
rateConfig(ratings[0].config, eglOpenGLBIT, true);
}
}
delete[] configs;
}
return configResult;
}
irr::s32 CEGLManager::rateConfig(EGLConfig config, EGLint eglOpenGLBIT, bool log)
{
// some values must be there or we ignore the config
#ifdef EGL_VERSION_1_3
EGLint attribRenderableType = 0;
eglGetConfigAttrib( EglDisplay, config, EGL_RENDERABLE_TYPE, &attribRenderableType);
if ( attribRenderableType != eglOpenGLBIT )
{
if ( log )
os::Printer::log("EGL_RENDERABLE_TYPE != eglOpenGLBIT");
return -1;
}
#endif
EGLint attribSurfaceType = 0;
eglGetConfigAttrib( EglDisplay, config, EGL_SURFACE_TYPE, &attribSurfaceType);
if ( attribSurfaceType != EGL_WINDOW_BIT )
{
if ( log )
os::Printer::log("EGL_SURFACE_TYPE!= EGL_WINDOW_BIT");
return -1;
}
// Generally we give a really bad rating if attributes are worse than requested
// We give a slight worse rating if attributes are not exact as requested
// And we use some priorities which might make sense (but not really fine-tuned,
// so if you think other priorities would be better don't worry about changing the values.
int rating = 0;
EGLint attribBufferSize = 0;
eglGetConfigAttrib( EglDisplay, config, EGL_BUFFER_SIZE, &attribBufferSize);
if ( attribBufferSize < Params.Bits )
{
if ( log )
os::Printer::log("No full color buffer.");
rating += 100;
}
if ( attribBufferSize > Params.Bits )
{
if ( log )
os::Printer::log("Larger color buffer.", ELL_DEBUG);
++rating;
}
EGLint attribRedSize = 0;
eglGetConfigAttrib( EglDisplay, config, EGL_RED_SIZE, &attribRedSize);
if ( attribRedSize < 5 && Params.Bits >= 4 )
rating += 100;
else if ( attribRedSize < 8 && Params.Bits >= 24)
rating += 10;
else if ( attribRedSize >= 8 && Params.Bits < 24 )
rating ++;
EGLint attribGreenSize = 0;
eglGetConfigAttrib( EglDisplay, config, EGL_GREEN_SIZE, &attribGreenSize);
if ( attribGreenSize < 5 && Params.Bits >= 4 )
rating += 100;
else if ( attribGreenSize < 8 && Params.Bits >= 24)
rating += 10;
else if ( attribGreenSize >= 8 && Params.Bits < 24 )
rating ++;
EGLint attribBlueSize = 0;
eglGetConfigAttrib( EglDisplay, config, EGL_BLUE_SIZE, &attribBlueSize);
if ( attribBlueSize < 5 && Params.Bits >= 4 )
rating += 100;
else if ( attribBlueSize < 8 && Params.Bits >= 24)
rating += 10;
else if ( attribBlueSize >= 8 && Params.Bits < 24 )
rating ++;
EGLint attribAlphaSize = 0;
eglGetConfigAttrib( EglDisplay, config, EGL_ALPHA_SIZE, &attribAlphaSize);
if ( Params.WithAlphaChannel && attribAlphaSize == 0 )
{
if ( log )
os::Printer::log("No alpha.");
rating += 10;
}
else if ( !Params.WithAlphaChannel && attribAlphaSize > 0 )
{
if ( log )
os::Printer::log("Got alpha (unrequested).", ELL_DEBUG);
rating ++;
}
EGLint attribStencilSize = 0;
eglGetConfigAttrib( EglDisplay, config, EGL_STENCIL_SIZE, &attribStencilSize);
if ( Params.Stencilbuffer && attribStencilSize == 0 )
{
if ( log )
os::Printer::log("No stencil buffer.");
rating += 10;
}
else if ( !Params.Stencilbuffer && attribStencilSize > 0 )
{
if ( log )
os::Printer::log("Got a stencil buffer (unrequested).", ELL_DEBUG);
rating ++;
}
EGLint attribDepthSize = 0;
eglGetConfigAttrib( EglDisplay, config, EGL_DEPTH_SIZE, &attribDepthSize);
if ( attribDepthSize < Params.ZBufferBits )
{
if ( log )
{
if (attribDepthSize > 0)
os::Printer::log("No full depth buffer.");
else
os::Printer::log("No depth buffer.");
}
rating += 50;
}
else if ( attribDepthSize != Params.ZBufferBits )
{
if ( log )
{
if ( Params.ZBufferBits == 0 )
os::Printer::log("Got a depth buffer (unrequested).", ELL_DEBUG);
else
os::Printer::log("Got a larger depth buffer.", ELL_DEBUG);
}
rating ++;
}
EGLint attribSampleBuffers=0, attribSamples = 0;
eglGetConfigAttrib( EglDisplay, config, EGL_SAMPLE_BUFFERS, &attribSampleBuffers);
eglGetConfigAttrib( EglDisplay, config, EGL_SAMPLES, &attribSamples);
if ( Params.AntiAlias && attribSampleBuffers == 0 )
{
if ( log )
os::Printer::log("No multisampling.");
rating += 20;
}
else if ( Params.AntiAlias && attribSampleBuffers && attribSamples < Params.AntiAlias )
{
if ( log )
os::Printer::log("Multisampling with less samples than requested.", ELL_DEBUG);
rating += 10;
}
else if ( Params.AntiAlias && attribSampleBuffers && attribSamples > Params.AntiAlias )
{
if ( log )
os::Printer::log("Multisampling with more samples than requested.", ELL_DEBUG);
rating += 5;
}
else if ( !Params.AntiAlias && attribSampleBuffers > 0 )
{
if ( log )
os::Printer::log("Got multisampling (unrequested).", ELL_DEBUG);
rating += 3;
}
return rating;
}
void CEGLManager::destroySurface()
{
if (EglSurface == EGL_NO_SURFACE)
return;
// We should unbind current EGL context before destroy EGL surface.
eglMakeCurrent(EglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface(EglDisplay, EglSurface);
EglSurface = EGL_NO_SURFACE;
}
bool CEGLManager::generateContext()
{
if (EglDisplay == EGL_NO_DISPLAY || EglSurface == EGL_NO_SURFACE)
return false;
if (EglContext != EGL_NO_CONTEXT)
return true;
EGLint OpenGLESVersion = 0;
switch (Params.DriverType)
{
case EDT_OGLES1:
OpenGLESVersion = 1;
break;
case EDT_OGLES2:
case EDT_WEBGL1:
OpenGLESVersion = 2;
break;
default:
break;
}
EGLint ContextAttrib[] =
{
#ifdef EGL_VERSION_1_3
EGL_CONTEXT_CLIENT_VERSION, OpenGLESVersion,
#endif
EGL_NONE, 0
};
EglContext = eglCreateContext(EglDisplay, EglConfig, EGL_NO_CONTEXT, ContextAttrib);
if (testEGLError())
{
os::Printer::log("Could not create EGL context.", ELL_ERROR);
return false;
}
os::Printer::log("EGL context created with OpenGLESVersion: ", core::stringc((int)OpenGLESVersion), ELL_DEBUG);
return true;
}
void CEGLManager::destroyContext()
{
if (EglContext == EGL_NO_CONTEXT)
return;
// We must unbind current EGL context before destroy it.
eglMakeCurrent(EglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(EglDisplay, EglContext);
EglContext = EGL_NO_CONTEXT;
}
bool CEGLManager::activateContext(const SExposedVideoData& videoData, bool restorePrimaryOnZero)
{
eglMakeCurrent(EglDisplay, EglSurface, EglSurface, EglContext);
if (testEGLError())
{
os::Printer::log("Could not make EGL context current.");
return false;
}
return true;
}
const SExposedVideoData& CEGLManager::getContext() const
{
return Data;
}
void* CEGLManager::getProcAddress(const std::string &procName)
{
void* proc = NULL;
proc = (void*)eglGetProcAddress(procName.c_str());
if (!proc) { // fallback
if (!libHandle)
libHandle = dlopen("libGLESv2.so", RTLD_LAZY);
if (libHandle)
proc = dlsym(libHandle, procName.c_str());
}
return proc;
}
bool CEGLManager::swapBuffers()
{
return (eglSwapBuffers(EglDisplay, EglSurface)==EGL_TRUE);
}
bool CEGLManager::testEGLError()
{
#if defined(EGL_VERSION_1_0) && defined(_DEBUG)
EGLint status = eglGetError();
switch (status)
{
case EGL_SUCCESS:
return false;
case EGL_NOT_INITIALIZED :
os::Printer::log("Not Initialized", ELL_ERROR);
break;
case EGL_BAD_ACCESS:
os::Printer::log("Bad Access", ELL_ERROR);
break;
case EGL_BAD_ALLOC:
os::Printer::log("Bad Alloc", ELL_ERROR);
break;
case EGL_BAD_ATTRIBUTE:
os::Printer::log("Bad Attribute", ELL_ERROR);
break;
case EGL_BAD_CONTEXT:
os::Printer::log("Bad Context", ELL_ERROR);
break;
case EGL_BAD_CONFIG:
os::Printer::log("Bad Config", ELL_ERROR);
break;
case EGL_BAD_CURRENT_SURFACE:
os::Printer::log("Bad Current Surface", ELL_ERROR);
break;
case EGL_BAD_DISPLAY:
os::Printer::log("Bad Display", ELL_ERROR);
break;
case EGL_BAD_SURFACE:
os::Printer::log("Bad Surface", ELL_ERROR);
break;
case EGL_BAD_MATCH:
os::Printer::log("Bad Match", ELL_ERROR);
break;
case EGL_BAD_PARAMETER:
os::Printer::log("Bad Parameter", ELL_ERROR);
break;
case EGL_BAD_NATIVE_PIXMAP:
os::Printer::log("Bad Native Pixmap", ELL_ERROR);
break;
case EGL_BAD_NATIVE_WINDOW:
os::Printer::log("Bad Native Window", ELL_ERROR);
break;
case EGL_CONTEXT_LOST:
os::Printer::log("Context Lost", ELL_ERROR);
break;
default:
break;
};
return true;
#else
return false;
#endif
}
}
}
#endif