23277066a4
Added a fallback with basic configuration if requested config failed. Also better check if choose config is success (num_configs can be >0 even if egl config is null)
596 lines
17 KiB
C++
596 lines
17 KiB
C++
// Copyright (C) 2013 Patryk Nadrowski
|
|
// Copyright (C) 2016-2017 Dawid Gan
|
|
// Heavily based on the OpenGL driver implemented by Nikolaus Gebhardt
|
|
// OpenGL ES driver implemented by Christian Stehno and first OpenGL ES 2.0
|
|
// driver implemented by Amundis.
|
|
// This file is part of the "Irrlicht Engine".
|
|
// For conditions of distribution and use, see copyright notice in Irrlicht.h
|
|
|
|
#include "IrrCompileConfig.h"
|
|
|
|
#if defined(_IRR_COMPILE_WITH_EGL_)
|
|
|
|
#include <cassert>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <vector>
|
|
|
|
#ifdef _IRR_COMPILE_WITH_ANDROID_DEVICE_
|
|
#include <android_native_app_glue.h>
|
|
#endif
|
|
|
|
#include "CContextEGL.h"
|
|
#include "os.h"
|
|
|
|
using namespace irr;
|
|
|
|
ContextManagerEGL::ContextManagerEGL()
|
|
{
|
|
m_egl_window = 0;
|
|
m_egl_display = EGL_NO_DISPLAY;
|
|
m_egl_surface = EGL_NO_SURFACE;
|
|
m_egl_context = EGL_NO_CONTEXT;
|
|
m_egl_config = 0;
|
|
m_egl_version = 0;
|
|
m_is_legacy_device = false;
|
|
m_initialized = false;
|
|
|
|
memset(&m_creation_params, 0, sizeof(ContextEGLParams));
|
|
}
|
|
|
|
|
|
ContextManagerEGL::~ContextManagerEGL()
|
|
{
|
|
close();
|
|
}
|
|
|
|
|
|
bool ContextManagerEGL::init(const ContextEGLParams& params)
|
|
{
|
|
if (m_initialized)
|
|
return false;
|
|
|
|
m_creation_params = params;
|
|
m_egl_window = m_creation_params.window;
|
|
|
|
bool success = initDisplay();
|
|
|
|
if (!success)
|
|
{
|
|
os::Printer::log("Error: Could not initialize EGL display.\n");
|
|
close();
|
|
return false;
|
|
}
|
|
|
|
bool has_minimum_requirements = false;
|
|
|
|
if (m_creation_params.opengl_api == CEGL_API_OPENGL)
|
|
{
|
|
if (hasEGLExtension("EGL_KHR_create_context") || m_egl_version >= 150)
|
|
{
|
|
has_minimum_requirements = true;
|
|
eglBindAPI(EGL_OPENGL_API);
|
|
}
|
|
}
|
|
else if (m_creation_params.opengl_api == CEGL_API_OPENGL_ES)
|
|
{
|
|
if (m_egl_version >= 130)
|
|
{
|
|
has_minimum_requirements = true;
|
|
eglBindAPI(EGL_OPENGL_ES_API);
|
|
}
|
|
}
|
|
|
|
if (!has_minimum_requirements)
|
|
{
|
|
os::Printer::log("Error: EGL version is too old.\n");
|
|
close();
|
|
return false;
|
|
}
|
|
|
|
success = chooseConfig();
|
|
|
|
if (!success)
|
|
{
|
|
os::Printer::log("Error: Couldn't get EGL config.\n");
|
|
close();
|
|
return false;
|
|
}
|
|
|
|
success = createSurface();
|
|
|
|
if (!success)
|
|
{
|
|
os::Printer::log("Error: Couldn't create EGL surface.\n");
|
|
close();
|
|
return false;
|
|
}
|
|
|
|
success = createContext();
|
|
|
|
if (!success)
|
|
{
|
|
os::Printer::log("Error: Couldn't create OpenGL context.\n");
|
|
close();
|
|
return false;
|
|
}
|
|
|
|
success = eglMakeCurrent(m_egl_display, m_egl_surface, m_egl_surface,
|
|
m_egl_context);
|
|
|
|
if (!success)
|
|
{
|
|
checkEGLError();
|
|
os::Printer::log("Error: Couldn't make context current for EGL display.\n");
|
|
close();
|
|
return false;
|
|
}
|
|
|
|
eglSwapInterval(m_egl_display, m_creation_params.vsync_enabled ? 1 : 0);
|
|
|
|
m_initialized = true;
|
|
return true;
|
|
}
|
|
|
|
|
|
bool ContextManagerEGL::initDisplay()
|
|
{
|
|
EGLNativeDisplayType display = m_creation_params.display;
|
|
|
|
#ifdef _IRR_COMPILE_WITH_ANDROID_DEVICE_
|
|
display = EGL_DEFAULT_DISPLAY;
|
|
#endif
|
|
|
|
if (display != EGL_DEFAULT_DISPLAY)
|
|
{
|
|
m_egl_display = eglGetDisplay(display);
|
|
}
|
|
|
|
if (m_egl_display == EGL_NO_DISPLAY)
|
|
{
|
|
m_egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
|
}
|
|
|
|
if (m_egl_display == EGL_NO_DISPLAY)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int egl_version_major = 0;
|
|
int egl_version_minor = 0;
|
|
|
|
bool success = eglInitialize(m_egl_display, &egl_version_major,
|
|
&egl_version_minor);
|
|
|
|
if (success)
|
|
{
|
|
m_egl_version = 100 * egl_version_major + 10 * egl_version_minor;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
|
|
bool ContextManagerEGL::chooseConfig()
|
|
{
|
|
std::vector<EGLint> config_attribs;
|
|
config_attribs.push_back(EGL_RED_SIZE);
|
|
config_attribs.push_back(8);
|
|
config_attribs.push_back(EGL_GREEN_SIZE);
|
|
config_attribs.push_back(8);
|
|
config_attribs.push_back(EGL_BLUE_SIZE);
|
|
config_attribs.push_back(8);
|
|
config_attribs.push_back(EGL_ALPHA_SIZE);
|
|
config_attribs.push_back(m_creation_params.with_alpha_channel ? 8 : 0);
|
|
config_attribs.push_back(EGL_DEPTH_SIZE);
|
|
config_attribs.push_back(16);
|
|
// config_attribs.push_back(EGL_BUFFER_SIZE);
|
|
// config_attribs.push_back(24);
|
|
// config_attribs.push_back(EGL_STENCIL_SIZE);
|
|
// config_attribs.push_back(stencil_buffer);
|
|
// config_attribs.push_back(EGL_SAMPLE_BUFFERS);
|
|
// config_attribs.push_back(antialias ? 1 : 0);
|
|
// config_attribs.push_back(EGL_SAMPLES);
|
|
// config_attribs.push_back(antialias);
|
|
|
|
if (m_creation_params.opengl_api == CEGL_API_OPENGL)
|
|
{
|
|
config_attribs.push_back(EGL_RENDERABLE_TYPE);
|
|
config_attribs.push_back(EGL_OPENGL_BIT);
|
|
}
|
|
else if (m_creation_params.opengl_api == CEGL_API_OPENGL_ES)
|
|
{
|
|
config_attribs.push_back(EGL_RENDERABLE_TYPE);
|
|
config_attribs.push_back(EGL_OPENGL_ES2_BIT);
|
|
}
|
|
|
|
if (m_creation_params.surface_type == CEGL_SURFACE_WINDOW)
|
|
{
|
|
config_attribs.push_back(EGL_SURFACE_TYPE);
|
|
config_attribs.push_back(EGL_WINDOW_BIT);
|
|
}
|
|
else if (m_creation_params.surface_type == CEGL_SURFACE_PBUFFER)
|
|
{
|
|
config_attribs.push_back(EGL_SURFACE_TYPE);
|
|
config_attribs.push_back(EGL_PBUFFER_BIT);
|
|
}
|
|
|
|
config_attribs.push_back(EGL_NONE);
|
|
config_attribs.push_back(0);
|
|
|
|
EGLint num_configs = 0;
|
|
|
|
bool success = eglChooseConfig(m_egl_display, &config_attribs[0],
|
|
&m_egl_config, 1, &num_configs);
|
|
|
|
if (!success || m_egl_config == NULL || num_configs < 1)
|
|
{
|
|
config_attribs[1] = 5; //EGL_RED_SIZE
|
|
config_attribs[3] = 6; //EGL_GREEN_SIZE
|
|
config_attribs[5] = 5; //EGL_BLUE_SIZE
|
|
config_attribs[7] = 0; //EGL_ALPHA_SIZE
|
|
config_attribs[9] = 1; //EGL_DEPTH_SIZE
|
|
|
|
success = eglChooseConfig(m_egl_display, &config_attribs[0],
|
|
&m_egl_config, 1, &num_configs);
|
|
}
|
|
|
|
if (!success || m_egl_config == NULL || num_configs < 1)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#ifdef _IRR_COMPILE_WITH_ANDROID_DEVICE_
|
|
EGLint format = 0;
|
|
eglGetConfigAttrib(m_egl_display, m_egl_config, EGL_NATIVE_VISUAL_ID,
|
|
&format);
|
|
ANativeWindow_setBuffersGeometry(m_egl_window, 0, 0, format);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool ContextManagerEGL::createSurface()
|
|
{
|
|
if (m_creation_params.surface_type == CEGL_SURFACE_WINDOW)
|
|
{
|
|
if (m_egl_surface == EGL_NO_SURFACE)
|
|
{
|
|
m_egl_surface = eglCreateWindowSurface(m_egl_display, m_egl_config,
|
|
m_egl_window, NULL);
|
|
}
|
|
|
|
if (m_egl_surface == EGL_NO_SURFACE)
|
|
{
|
|
m_egl_surface = eglCreateWindowSurface(m_egl_display, m_egl_config,
|
|
0, NULL);
|
|
}
|
|
}
|
|
else if (m_creation_params.surface_type == CEGL_SURFACE_PBUFFER)
|
|
{
|
|
if (m_egl_surface == EGL_NO_SURFACE)
|
|
{
|
|
std::vector<EGLint> pbuffer_attribs;
|
|
pbuffer_attribs.push_back(EGL_WIDTH);
|
|
pbuffer_attribs.push_back(m_creation_params.pbuffer_width);
|
|
pbuffer_attribs.push_back(EGL_HEIGHT);
|
|
pbuffer_attribs.push_back(m_creation_params.pbuffer_height);
|
|
pbuffer_attribs.push_back(EGL_NONE);
|
|
pbuffer_attribs.push_back(0);
|
|
|
|
m_egl_surface = eglCreatePbufferSurface(m_egl_display,
|
|
m_egl_config,
|
|
&pbuffer_attribs[0]);
|
|
}
|
|
|
|
if (m_egl_surface == EGL_NO_SURFACE)
|
|
{
|
|
std::vector<EGLint> pbuffer_attribs;
|
|
pbuffer_attribs.push_back(EGL_WIDTH);
|
|
pbuffer_attribs.push_back(m_creation_params.pbuffer_width);
|
|
pbuffer_attribs.push_back(EGL_HEIGHT);
|
|
pbuffer_attribs.push_back(m_creation_params.pbuffer_height);
|
|
pbuffer_attribs.push_back(EGL_LARGEST_PBUFFER);
|
|
pbuffer_attribs.push_back(EGL_TRUE);
|
|
pbuffer_attribs.push_back(EGL_NONE);
|
|
pbuffer_attribs.push_back(0);
|
|
|
|
m_egl_surface = eglCreatePbufferSurface(m_egl_display,
|
|
m_egl_config,
|
|
&pbuffer_attribs[0]);
|
|
}
|
|
}
|
|
|
|
return m_egl_surface != EGL_NO_SURFACE;
|
|
}
|
|
|
|
|
|
bool ContextManagerEGL::createContext()
|
|
{
|
|
m_is_legacy_device = false;
|
|
|
|
if (m_creation_params.opengl_api == CEGL_API_OPENGL_ES)
|
|
{
|
|
if (!m_creation_params.force_legacy_device)
|
|
{
|
|
if (m_egl_context == EGL_NO_CONTEXT)
|
|
{
|
|
std::vector<EGLint> context_attribs;
|
|
context_attribs.push_back(EGL_CONTEXT_CLIENT_VERSION);
|
|
context_attribs.push_back(3);
|
|
context_attribs.push_back(EGL_NONE);
|
|
context_attribs.push_back(0);
|
|
|
|
m_egl_context = eglCreateContext(m_egl_display,
|
|
m_egl_config,
|
|
EGL_NO_CONTEXT,
|
|
&context_attribs[0]);
|
|
}
|
|
}
|
|
|
|
if (m_egl_context == EGL_NO_CONTEXT)
|
|
{
|
|
m_is_legacy_device = true;
|
|
|
|
std::vector<EGLint> context_attribs;
|
|
context_attribs.push_back(EGL_CONTEXT_CLIENT_VERSION);
|
|
context_attribs.push_back(2);
|
|
context_attribs.push_back(EGL_NONE);
|
|
context_attribs.push_back(0);
|
|
|
|
m_egl_context = eglCreateContext(m_egl_display,
|
|
m_egl_config,
|
|
EGL_NO_CONTEXT,
|
|
&context_attribs[0]);
|
|
}
|
|
}
|
|
else if (m_creation_params.opengl_api == CEGL_API_OPENGL)
|
|
{
|
|
if (!m_creation_params.force_legacy_device)
|
|
{
|
|
if (m_egl_context == EGL_NO_CONTEXT)
|
|
{
|
|
std::vector<EGLint> context_attribs;
|
|
context_attribs.push_back(EGL_CONTEXT_MAJOR_VERSION);
|
|
context_attribs.push_back(4);
|
|
context_attribs.push_back(EGL_CONTEXT_MINOR_VERSION);
|
|
context_attribs.push_back(3);
|
|
context_attribs.push_back(EGL_NONE);
|
|
context_attribs.push_back(0);
|
|
|
|
m_egl_context = eglCreateContext(m_egl_display,
|
|
m_egl_config,
|
|
EGL_NO_CONTEXT,
|
|
&context_attribs[0]);
|
|
}
|
|
|
|
if (m_egl_context == EGL_NO_CONTEXT)
|
|
{
|
|
std::vector<EGLint> context_attribs;
|
|
context_attribs.push_back(EGL_CONTEXT_MAJOR_VERSION);
|
|
context_attribs.push_back(3);
|
|
context_attribs.push_back(EGL_CONTEXT_MINOR_VERSION);
|
|
context_attribs.push_back(3);
|
|
context_attribs.push_back(EGL_NONE);
|
|
context_attribs.push_back(0);
|
|
|
|
m_egl_context = eglCreateContext(m_egl_display,
|
|
m_egl_config,
|
|
EGL_NO_CONTEXT,
|
|
&context_attribs[0]);
|
|
}
|
|
|
|
if (m_egl_context == EGL_NO_CONTEXT)
|
|
{
|
|
std::vector<EGLint> context_attribs;
|
|
context_attribs.push_back(EGL_CONTEXT_MAJOR_VERSION);
|
|
context_attribs.push_back(3);
|
|
context_attribs.push_back(EGL_CONTEXT_MINOR_VERSION);
|
|
context_attribs.push_back(1);
|
|
context_attribs.push_back(EGL_NONE);
|
|
context_attribs.push_back(0);
|
|
|
|
m_egl_context = eglCreateContext(m_egl_display,
|
|
m_egl_config,
|
|
EGL_NO_CONTEXT,
|
|
&context_attribs[0]);
|
|
}
|
|
}
|
|
|
|
if (m_egl_context == EGL_NO_CONTEXT)
|
|
{
|
|
m_is_legacy_device = true;
|
|
|
|
std::vector<EGLint> context_attribs;
|
|
context_attribs.push_back(EGL_CONTEXT_MAJOR_VERSION);
|
|
context_attribs.push_back(2);
|
|
context_attribs.push_back(EGL_CONTEXT_MINOR_VERSION);
|
|
context_attribs.push_back(1);
|
|
context_attribs.push_back(EGL_NONE);
|
|
context_attribs.push_back(0);
|
|
|
|
m_egl_context = eglCreateContext(m_egl_display,
|
|
m_egl_config,
|
|
EGL_NO_CONTEXT,
|
|
&context_attribs[0]);
|
|
}
|
|
}
|
|
|
|
return m_egl_context != EGL_NO_CONTEXT;
|
|
}
|
|
|
|
|
|
void ContextManagerEGL::close()
|
|
{
|
|
eglMakeCurrent(m_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
|
EGL_NO_CONTEXT);
|
|
|
|
if (m_egl_context != EGL_NO_CONTEXT)
|
|
{
|
|
eglDestroyContext(m_egl_display, m_egl_context);
|
|
m_egl_context = EGL_NO_CONTEXT;
|
|
}
|
|
|
|
if (m_egl_surface != EGL_NO_SURFACE)
|
|
{
|
|
eglDestroySurface(m_egl_display, m_egl_surface);
|
|
m_egl_surface = EGL_NO_SURFACE;
|
|
}
|
|
|
|
if (m_egl_display != EGL_NO_DISPLAY)
|
|
{
|
|
eglTerminate(m_egl_display);
|
|
m_egl_display = EGL_NO_DISPLAY;
|
|
}
|
|
}
|
|
|
|
|
|
bool ContextManagerEGL::swapBuffers()
|
|
{
|
|
bool success = eglSwapBuffers(m_egl_display, m_egl_surface);
|
|
|
|
#ifdef DEBUG
|
|
if (!success)
|
|
{
|
|
eglGetError();
|
|
}
|
|
#endif
|
|
|
|
return success;
|
|
}
|
|
|
|
|
|
bool ContextManagerEGL::makeCurrent()
|
|
{
|
|
bool success = eglMakeCurrent(m_egl_display, m_egl_surface, m_egl_surface,
|
|
m_egl_context);
|
|
|
|
return success;
|
|
}
|
|
|
|
|
|
void ContextManagerEGL::reloadEGLSurface(void* window)
|
|
{
|
|
if (!m_initialized)
|
|
return;
|
|
|
|
#ifdef _IRR_COMPILE_WITH_ANDROID_DEVICE_
|
|
m_egl_window = (ANativeWindow*)window;
|
|
|
|
if (!m_egl_window)
|
|
{
|
|
os::Printer::log("Error: Invalid EGL window.\n");
|
|
}
|
|
#endif
|
|
|
|
eglMakeCurrent(m_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
|
EGL_NO_CONTEXT);
|
|
|
|
eglDestroySurface(m_egl_display, m_egl_surface);
|
|
m_egl_surface = EGL_NO_SURFACE;
|
|
|
|
bool success = createSurface();
|
|
|
|
if (!success)
|
|
{
|
|
os::Printer::log("Error: Could not create EGL surface.");
|
|
}
|
|
|
|
success = eglMakeCurrent(m_egl_display, m_egl_surface, m_egl_surface,
|
|
m_egl_context);
|
|
|
|
if (!success)
|
|
{
|
|
checkEGLError();
|
|
os::Printer::log("Error: Couldn't make context current for EGL display.\n");
|
|
}
|
|
}
|
|
|
|
|
|
bool ContextManagerEGL::getSurfaceDimensions(int* width, int* height)
|
|
{
|
|
if (!eglQuerySurface(m_egl_display, m_egl_surface, EGL_WIDTH, width))
|
|
return false;
|
|
|
|
if (!eglQuerySurface(m_egl_display, m_egl_surface, EGL_HEIGHT, height))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool ContextManagerEGL::hasEGLExtension(const char* extension)
|
|
{
|
|
const char* extensions = eglQueryString(m_egl_display, EGL_EXTENSIONS);
|
|
|
|
if (extensions && strstr(extensions, extension) != NULL)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool ContextManagerEGL::checkEGLError()
|
|
{
|
|
bool result = true;
|
|
|
|
switch (eglGetError())
|
|
{
|
|
case EGL_SUCCESS:
|
|
result = false;
|
|
break;
|
|
case EGL_NOT_INITIALIZED:
|
|
os::Printer::log("Error: EGL_NOT_INITIALIZED\n");
|
|
break;
|
|
case EGL_BAD_ACCESS:
|
|
os::Printer::log("Error: EGL_BAD_ACCESS\n");
|
|
break;
|
|
case EGL_BAD_ALLOC:
|
|
os::Printer::log("Error: EGL_BAD_ALLOC\n");
|
|
break;
|
|
case EGL_BAD_ATTRIBUTE:
|
|
os::Printer::log("Error: EGL_BAD_ATTRIBUTE\n");
|
|
break;
|
|
case EGL_BAD_CONTEXT:
|
|
os::Printer::log("Error: EGL_BAD_CONTEXT\n");
|
|
break;
|
|
case EGL_BAD_CONFIG:
|
|
os::Printer::log("Error: EGL_BAD_CONFIG\n");
|
|
break;
|
|
case EGL_BAD_CURRENT_SURFACE:
|
|
os::Printer::log("Error: EGL_BAD_CURRENT_SURFACE\n");
|
|
break;
|
|
case EGL_BAD_DISPLAY:
|
|
os::Printer::log("Error: EGL_BAD_DISPLAY\n");
|
|
break;
|
|
case EGL_BAD_SURFACE:
|
|
os::Printer::log("Error: EGL_BAD_SURFACE\n");
|
|
break;
|
|
case EGL_BAD_MATCH:
|
|
os::Printer::log("Error: EGL_BAD_MATCH\n");
|
|
break;
|
|
case EGL_BAD_PARAMETER:
|
|
os::Printer::log("Error: EGL_BAD_PARAMETER\n");
|
|
break;
|
|
case EGL_BAD_NATIVE_PIXMAP:
|
|
os::Printer::log("Error: EGL_BAD_NATIVE_PIXMAP\n");
|
|
break;
|
|
case EGL_BAD_NATIVE_WINDOW:
|
|
os::Printer::log("Error: EGL_BAD_NATIVE_WINDOW\n");
|
|
break;
|
|
case EGL_CONTEXT_LOST:
|
|
os::Printer::log("Error: EGL_CONTEXT_LOST\n");
|
|
break;
|
|
default:
|
|
os::Printer::log("Error: Unknown EGL error.\n");
|
|
break;
|
|
};
|
|
|
|
return result;
|
|
}
|
|
|
|
#endif
|