Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'intern/ghost/intern/GHOST_ContextEGL.cpp')
-rw-r--r--intern/ghost/intern/GHOST_ContextEGL.cpp638
1 files changed, 638 insertions, 0 deletions
diff --git a/intern/ghost/intern/GHOST_ContextEGL.cpp b/intern/ghost/intern/GHOST_ContextEGL.cpp
new file mode 100644
index 00000000000..6a9231835c5
--- /dev/null
+++ b/intern/ghost/intern/GHOST_ContextEGL.cpp
@@ -0,0 +1,638 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2013 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Jason Wilkins
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file ghost/intern/GHOST_ContextEGL.cpp
+ * \ingroup GHOST
+ *
+ * Definition of GHOST_ContextEGL class.
+ */
+
+#include "GHOST_ContextEGL.h"
+
+#include <set>
+#include <sstream>
+#include <vector>
+
+#include <cassert>
+#include <cstdio>
+#include <cstring>
+
+
+#ifdef WITH_GLEW_MX
+EGLEWContext *eglewContext = NULL;
+#endif
+
+
+#define CASE_CODE_RETURN_STR(code) case code: return #code;
+
+static const char *get_egl_error_enum_string(EGLenum error)
+{
+ switch (error) {
+ CASE_CODE_RETURN_STR(EGL_SUCCESS)
+ CASE_CODE_RETURN_STR(EGL_NOT_INITIALIZED)
+ CASE_CODE_RETURN_STR(EGL_BAD_ACCESS)
+ CASE_CODE_RETURN_STR(EGL_BAD_ALLOC)
+ CASE_CODE_RETURN_STR(EGL_BAD_ATTRIBUTE)
+ CASE_CODE_RETURN_STR(EGL_BAD_CONTEXT)
+ CASE_CODE_RETURN_STR(EGL_BAD_CONFIG)
+ CASE_CODE_RETURN_STR(EGL_BAD_CURRENT_SURFACE)
+ CASE_CODE_RETURN_STR(EGL_BAD_DISPLAY)
+ CASE_CODE_RETURN_STR(EGL_BAD_SURFACE)
+ CASE_CODE_RETURN_STR(EGL_BAD_MATCH)
+ CASE_CODE_RETURN_STR(EGL_BAD_PARAMETER)
+ CASE_CODE_RETURN_STR(EGL_BAD_NATIVE_PIXMAP)
+ CASE_CODE_RETURN_STR(EGL_BAD_NATIVE_WINDOW)
+ CASE_CODE_RETURN_STR(EGL_CONTEXT_LOST)
+ default:
+ return NULL;
+ }
+}
+
+static const char *get_egl_error_message_string(EGLenum error)
+{
+ switch (error) {
+ case EGL_SUCCESS:
+ return "The last function succeeded without error.";
+
+ case EGL_NOT_INITIALIZED:
+ return ("EGL is not initialized, or could not be initialized, "
+ "for the specified EGL display connection.");
+
+ case EGL_BAD_ACCESS:
+ return ("EGL cannot access a requested resource "
+ "(for example a context is bound in another thread).");
+
+ case EGL_BAD_ALLOC:
+ return "EGL failed to allocate resources for the requested operation.";
+
+ case EGL_BAD_ATTRIBUTE:
+ return "An unrecognized attribute or attribute value was passed in the attribute list.";
+
+ case EGL_BAD_CONTEXT:
+ return "An EGLContext argument does not name a valid EGL rendering context.";
+
+ case EGL_BAD_CONFIG:
+ return "An EGLConfig argument does not name a valid EGL frame buffer configuration.";
+
+ case EGL_BAD_CURRENT_SURFACE:
+ return ("The current surface of the calling thread is a window, "
+ "pixel buffer or pixmap that is no longer valid.");
+
+ case EGL_BAD_DISPLAY:
+ return "An EGLDisplay argument does not name a valid EGL display connection.";
+
+ case EGL_BAD_SURFACE:
+ return ("An EGLSurface argument does not name a valid surface "
+ "(window, pixel buffer or pixmap) configured for GL rendering.");
+
+ case EGL_BAD_MATCH:
+ return ("Arguments are inconsistent "
+ "(for example, a valid context requires buffers not supplied by a valid surface).");
+
+ case EGL_BAD_PARAMETER:
+ return "One or more argument values are invalid.";
+
+ case EGL_BAD_NATIVE_PIXMAP:
+ return "A NativePixmapType argument does not refer to a valid native pixmap.";
+
+ case EGL_BAD_NATIVE_WINDOW:
+ return "A NativeWindowType argument does not refer to a valid native window.";
+
+ case EGL_CONTEXT_LOST:
+ return ("A power management event has occurred. "
+ "The application must destroy all contexts and reinitialise OpenGL ES state "
+ "and objects to continue rendering.");
+
+ default:
+ return NULL;
+ }
+}
+
+
+static bool egl_chk(bool result, const char *file = NULL, int line = 0, const char *text = NULL)
+{
+ if (!result) {
+ EGLenum error = eglGetError();
+
+ const char *code = get_egl_error_enum_string(error);
+ const char *msg = get_egl_error_message_string(error);
+
+#ifndef NDEBUG
+ fprintf(stderr,
+ "%s(%d):[%s] -> EGL Error (0x%04X): %s: %s\n",
+ file, line, text, error,
+ code ? code : "<Unknown>",
+ msg ? msg : "<Unknown>");
+#else
+ fprintf(stderr,
+ "EGL Error (0x%04X): %s: %s\n",
+ error,
+ code ? code : "<Unknown>",
+ msg ? msg : "<Unknown>");
+#endif
+ }
+
+ return result;
+}
+
+#ifndef NDEBUG
+#define EGL_CHK(x) egl_chk((x), __FILE__, __LINE__, #x)
+#else
+#define EGL_CHK(x) egl_chk(x)
+#endif
+
+
+static inline bool bindAPI(EGLenum api)
+{
+#ifdef WITH_GLEW_MX
+ if (eglewContext != NULL)
+#endif
+ {
+ if (EGLEW_VERSION_1_2) {
+ return (EGL_CHK(eglBindAPI(api)) == EGL_TRUE);
+ }
+ }
+
+ return false;
+}
+
+
+#ifdef WITH_GL_ANGLE
+HMODULE GHOST_ContextEGL::s_d3dcompiler = NULL;
+#endif
+
+
+EGLContext GHOST_ContextEGL::s_gl_sharedContext = EGL_NO_CONTEXT;
+EGLint GHOST_ContextEGL::s_gl_sharedCount = 0;
+
+EGLContext GHOST_ContextEGL::s_gles_sharedContext = EGL_NO_CONTEXT;
+EGLint GHOST_ContextEGL::s_gles_sharedCount = 0;
+
+EGLContext GHOST_ContextEGL::s_vg_sharedContext = EGL_NO_CONTEXT;
+EGLint GHOST_ContextEGL::s_vg_sharedCount = 0;
+
+
+#pragma warning(disable : 4715)
+
+template <typename T>
+T &choose_api(EGLenum api, T &a, T &b, T &c)
+{
+ switch (api) {
+ case EGL_OPENGL_API:
+ return a;
+ case EGL_OPENGL_ES_API:
+ return b;
+ case EGL_OPENVG_API:
+ return c;
+ default:
+ abort();
+ }
+}
+
+
+GHOST_ContextEGL::GHOST_ContextEGL(
+ bool stereoVisual,
+ GHOST_TUns16 numOfAASamples,
+ EGLNativeWindowType nativeWindow,
+ EGLNativeDisplayType nativeDisplay,
+ EGLint contextProfileMask,
+ EGLint contextMajorVersion,
+ EGLint contextMinorVersion,
+ EGLint contextFlags,
+ EGLint contextResetNotificationStrategy,
+ EGLenum api)
+ : GHOST_Context(stereoVisual, numOfAASamples),
+ m_nativeDisplay(nativeDisplay),
+ m_nativeWindow (nativeWindow),
+ m_contextProfileMask(contextProfileMask),
+ m_contextMajorVersion(contextMajorVersion),
+ m_contextMinorVersion(contextMinorVersion),
+ m_contextFlags(contextFlags),
+ m_contextResetNotificationStrategy(contextResetNotificationStrategy),
+ m_api(api),
+ m_context(EGL_NO_CONTEXT),
+ m_surface(EGL_NO_SURFACE),
+ m_display(EGL_NO_DISPLAY),
+ m_swap_interval(1),
+#ifdef WITH_GLEW_MX
+ m_eglewContext(NULL),
+#endif
+ m_sharedContext(choose_api(api, s_gl_sharedContext, s_gles_sharedContext, s_vg_sharedContext)),
+ m_sharedCount (choose_api(api, s_gl_sharedCount, s_gles_sharedCount, s_vg_sharedCount))
+{
+ assert(m_nativeWindow != 0);
+ assert(m_nativeDisplay != NULL);
+}
+
+
+GHOST_ContextEGL::~GHOST_ContextEGL()
+{
+ if (m_display != EGL_NO_DISPLAY) {
+ activateEGLEW();
+
+ bindAPI(m_api);
+
+ if (m_context != EGL_NO_CONTEXT) {
+ if (m_context == ::eglGetCurrentContext())
+ EGL_CHK(::eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
+
+ if (m_context != m_sharedContext || m_sharedCount == 1) {
+ assert(m_sharedCount > 0);
+
+ m_sharedCount--;
+
+ if (m_sharedCount == 0)
+ m_sharedContext = EGL_NO_CONTEXT;
+
+ EGL_CHK(::eglDestroyContext(m_display, m_context));
+ }
+ }
+
+ if (m_surface != EGL_NO_SURFACE)
+ EGL_CHK(::eglDestroySurface(m_display, m_surface));
+
+ EGL_CHK(::eglTerminate(m_display));
+
+#ifdef WITH_GLEW_MX
+ delete m_eglewContext;
+#endif
+ }
+}
+
+
+GHOST_TSuccess GHOST_ContextEGL::swapBuffers()
+{
+ return EGL_CHK(::eglSwapBuffers(m_display, m_surface)) ? GHOST_kSuccess : GHOST_kFailure;
+}
+
+
+GHOST_TSuccess GHOST_ContextEGL::setSwapInterval(int interval)
+{
+ if (EGLEW_VERSION_1_1) {
+ if (EGL_CHK(::eglSwapInterval(m_display, interval))) {
+ m_swap_interval = interval;
+
+ return GHOST_kSuccess;
+ }
+ else {
+ return GHOST_kFailure;
+ }
+ }
+ else {
+ return GHOST_kFailure;
+ }
+}
+
+
+GHOST_TSuccess GHOST_ContextEGL::getSwapInterval(int &intervalOut)
+{
+ intervalOut = m_swap_interval; // XXX jwilkins: make sure there is no way to query this?
+
+ return GHOST_kSuccess;
+}
+
+
+GHOST_TSuccess GHOST_ContextEGL::activateDrawingContext()
+{
+ if (m_display) {
+ activateEGLEW();
+ activateGLEW();
+
+ bindAPI(m_api);
+
+ return EGL_CHK(::eglMakeCurrent(m_display, m_surface, m_surface, m_context)) ? GHOST_kSuccess : GHOST_kFailure;
+ }
+ else {
+ return GHOST_kFailure;
+ }
+}
+
+
+void GHOST_ContextEGL::initContextEGLEW()
+{
+#ifdef WITH_GLEW_MX
+ eglewContext = new EGLEWContext;
+ memset(eglewContext, 0, sizeof(EGLEWContext));
+
+ delete m_eglewContext;
+ m_eglewContext = eglewContext;
+#endif
+
+ if (GLEW_CHK(eglewInit(m_display)) != GLEW_OK)
+ fprintf(stderr, "Warning! EGLEW failed to initialize properly.\n");
+}
+
+
+static const std::string &api_string(EGLenum api)
+{
+ static const std::string a("OpenGL");
+ static const std::string b("OpenGL ES");
+ static const std::string c("OpenVG");
+
+ return choose_api(api, a, b, c);
+}
+
+GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
+{
+ // objects have to be declared here due to the use of goto
+ std::vector<EGLint> attrib_list;
+ EGLint num_config = 0;
+
+ if (m_stereoVisual)
+ fprintf(stderr, "Warning! Stereo OpenGL ES contexts are not supported.\n");
+
+ m_stereoVisual = false; // It doesn't matter what the Window wants.
+
+#ifdef WITH_GL_ANGLE
+ // d3dcompiler_XX.dll needs to be loaded before ANGLE will work
+ if (s_d3dcompiler == NULL) {
+ s_d3dcompiler = LoadLibrary(D3DCOMPILER);
+
+ WIN32_CHK(s_d3dcompiler != NULL);
+
+ if (s_d3dcompiler == NULL) {
+ fprintf(stderr, "LoadLibrary(\"" D3DCOMPILER "\") failed!\n");
+ return GHOST_kFailure;
+ }
+ }
+#endif
+
+ EGLDisplay prev_display = eglGetCurrentDisplay();
+ EGLSurface prev_draw = eglGetCurrentSurface(EGL_DRAW);
+ EGLSurface prev_read = eglGetCurrentSurface(EGL_READ);
+ EGLContext prev_context = eglGetCurrentContext();
+
+ m_display = ::eglGetDisplay(m_nativeDisplay);
+
+ if (!EGL_CHK(m_display != EGL_NO_DISPLAY))
+ return GHOST_kFailure;
+
+ EGLint egl_major, egl_minor;
+
+ if (!EGL_CHK(::eglInitialize(m_display, &egl_major, &egl_minor)))
+ goto error;
+
+ fprintf(stderr, "EGL Version %d.%d\n", egl_major, egl_minor);
+
+ if (!EGL_CHK(::eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)))
+ goto error;
+
+ initContextEGLEW();
+
+ if (!bindAPI(m_api))
+ goto error;
+
+
+ // build attribute list
+
+ attrib_list.reserve(20);
+
+ if (m_api == EGL_OPENGL_ES_API && EGLEW_VERSION_1_2) {
+ // According to the spec it seems that you are required to set EGL_RENDERABLE_TYPE,
+ // but some implementations (ANGLE) do not seem to care.
+
+ if (m_contextMajorVersion == 1) {
+ attrib_list.push_back(EGL_RENDERABLE_TYPE);
+ attrib_list.push_back(EGL_OPENGL_ES_BIT);
+ }
+ else if (m_contextMajorVersion == 2) {
+ attrib_list.push_back(EGL_RENDERABLE_TYPE);
+ attrib_list.push_back(EGL_OPENGL_ES2_BIT);
+ }
+ else if (m_contextMajorVersion == 3) {
+ attrib_list.push_back(EGL_RENDERABLE_TYPE);
+ attrib_list.push_back(EGL_OPENGL_ES3_BIT_KHR);
+ }
+ else {
+ fprintf(stderr,
+ "Warning! Unable to request an ES context of version %d.%d\n",
+ m_contextMajorVersion, m_contextMinorVersion);
+ }
+
+ if (!((m_contextMajorVersion == 1) ||
+ (m_contextMajorVersion == 2 && EGLEW_VERSION_1_3) ||
+ (m_contextMajorVersion == 3 && /*EGLEW_VERSION_1_4 &&*/ EGLEW_KHR_create_context) ||
+ (m_contextMajorVersion == 3 && EGLEW_VERSION_1_5)))
+ {
+ fprintf(stderr,
+ "Warning! May not be able to create a version %d.%d ES context with version %d.%d of EGL\n",
+ m_contextMajorVersion, m_contextMinorVersion, egl_major, egl_minor);
+ }
+ }
+
+ attrib_list.push_back(EGL_RED_SIZE);
+ attrib_list.push_back(8);
+
+ attrib_list.push_back(EGL_GREEN_SIZE);
+ attrib_list.push_back(8);
+
+ attrib_list.push_back(EGL_BLUE_SIZE);
+ attrib_list.push_back(8);
+
+#ifdef GHOST_OPENGL_ALPHA
+ attrib_list.push_back(EGL_ALPHA_SIZE);
+ attrib_list.push_back(8);
+#endif
+
+ attrib_list.push_back(EGL_DEPTH_SIZE);
+ attrib_list.push_back(24);
+
+#ifdef GHOST_OPENGL_STENCIL
+ attrib_list.push_back(EGL_STENCIL_SIZE);
+ attrib_list.push_back(8);
+#endif
+
+ if (m_numOfAASamples > 0) {
+ attrib_list.push_back(EGL_SAMPLE_BUFFERS);
+ attrib_list.push_back(1);
+
+ attrib_list.push_back(EGL_SAMPLES);
+ attrib_list.push_back(m_numOfAASamples);
+ }
+
+ attrib_list.push_back(EGL_NONE);
+
+ EGLConfig config;
+
+ if (!EGL_CHK(::eglChooseConfig(m_display, &(attrib_list[0]), &config, 1, &num_config)))
+ goto error;
+
+ // A common error is to assume that ChooseConfig worked because it returned EGL_TRUE
+ if (num_config != 1) // num_config should be exactly 1
+ goto error;
+
+ if (m_numOfAASamples > 0) {
+ EGLint actualSamples;
+
+ if (!EGL_CHK(::eglGetConfigAttrib(m_display, config, EGL_SAMPLE_BUFFERS, &actualSamples)))
+ goto error;
+
+ if (m_numOfAASamples != actualSamples) {
+ fprintf(stderr,
+ "Warning! Unable to find a multisample pixel format that supports exactly %d samples. "
+ "Substituting one that uses %d samples.\n",
+ m_numOfAASamples,
+ actualSamples);
+
+ m_numOfAASamples = (GHOST_TUns16)actualSamples;
+ }
+ }
+
+ m_surface = ::eglCreateWindowSurface(m_display, config, m_nativeWindow, NULL);
+
+ if (!EGL_CHK(m_surface != EGL_NO_SURFACE))
+ goto error;
+
+ attrib_list.clear();
+
+ if (EGLEW_VERSION_1_5 || EGLEW_KHR_create_context) {
+ if (m_api == EGL_OPENGL_API || m_api == EGL_OPENGL_ES_API) {
+ if (m_contextMajorVersion != 0) {
+ attrib_list.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
+ attrib_list.push_back(m_contextMajorVersion);
+ }
+
+ if (m_contextMinorVersion != 0) {
+ attrib_list.push_back(EGL_CONTEXT_MINOR_VERSION_KHR);
+ attrib_list.push_back(m_contextMinorVersion);
+ }
+
+ if (m_contextFlags != 0) {
+ attrib_list.push_back(EGL_CONTEXT_FLAGS_KHR);
+ attrib_list.push_back(m_contextFlags);
+ }
+ }
+ else {
+ if (m_contextMajorVersion != 0 || m_contextMinorVersion != 0) {
+ fprintf(stderr,
+ "Warning! Cannot request specific versions of %s contexts.",
+ api_string(m_api).c_str());
+ }
+
+ if (m_contextFlags != 0) {
+ fprintf(stderr,
+ "Warning! Flags cannot be set on %s contexts.",
+ api_string(m_api).c_str());
+ }
+ }
+
+ if (m_api == EGL_OPENGL_API) {
+ if (m_contextProfileMask != 0) {
+ attrib_list.push_back(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR);
+ attrib_list.push_back(m_contextProfileMask);
+ }
+ }
+ else {
+ if (m_contextProfileMask != 0)
+ fprintf(stderr,
+ "Warning! Cannot select profile for %s contexts.",
+ api_string(m_api).c_str());
+ }
+
+ if (m_api == EGL_OPENGL_API || EGLEW_VERSION_1_5) {
+ if (m_contextResetNotificationStrategy != 0) {
+ attrib_list.push_back(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR);
+ attrib_list.push_back(m_contextResetNotificationStrategy);
+ }
+ }
+ else {
+ if (m_contextResetNotificationStrategy != 0) {
+ fprintf(stderr,
+ "Warning! EGL %d.%d cannot set the reset notification strategy on %s contexts.",
+ egl_major, egl_minor, api_string(m_api).c_str());
+ }
+ }
+ }
+ else {
+ if (m_api == EGL_OPENGL_ES_API) {
+ if (m_contextMajorVersion != 0) {
+ attrib_list.push_back(EGL_CONTEXT_CLIENT_VERSION);
+ attrib_list.push_back(m_contextMajorVersion);
+ }
+ }
+ else {
+ if (m_contextMajorVersion != 0 || m_contextMinorVersion != 0) {
+ fprintf(stderr,
+ "Warning! EGL %d.%d is unable to select between versions of %s.",
+ egl_major, egl_minor, api_string(m_api).c_str());
+ }
+ }
+
+ if (m_contextFlags != 0) {
+ fprintf(stderr,
+ "Warning! EGL %d.%d is unable to set context flags.",
+ egl_major, egl_minor);
+ }
+ if (m_contextProfileMask != 0) {
+ fprintf(stderr,
+ "Warning! EGL %d.%d is unable to select between profiles.",
+ egl_major, egl_minor);
+ }
+ if (m_contextResetNotificationStrategy != 0) {
+ fprintf(stderr,
+ "Warning! EGL %d.%d is unable to set the reset notification strategies.",
+ egl_major, egl_minor);
+ }
+ }
+
+ attrib_list.push_back(EGL_NONE);
+
+ m_context = ::eglCreateContext(m_display, config, m_sharedContext, &(attrib_list[0]));
+
+ if (!EGL_CHK(m_context != EGL_NO_CONTEXT))
+ goto error;
+
+ if (m_sharedContext == EGL_NO_CONTEXT)
+ m_sharedContext = m_context;
+
+ m_sharedCount++;
+
+ if (!EGL_CHK(::eglMakeCurrent(m_display, m_surface, m_surface, m_context)))
+ goto error;
+
+ initContextGLEW();
+
+ initClearGL();
+ ::eglSwapBuffers(m_display, m_surface);
+
+ return GHOST_kSuccess;
+
+error:
+ if (prev_display != EGL_NO_DISPLAY)
+ EGL_CHK(eglMakeCurrent(prev_display, prev_draw, prev_read, prev_context));
+
+ return GHOST_kFailure;
+}
+
+
+GHOST_TSuccess GHOST_ContextEGL::releaseNativeHandles()
+{
+ m_nativeWindow = 0;
+ m_nativeDisplay = NULL;
+
+ return GHOST_kSuccess;
+}