blender/intern/ghost/intern/GHOST_ContextEGL.cpp
Jason Wilkins 88fe896243 Checked each of my (jwilkins) XXX notes.
The ones in extern/glew-es have been changed to NOTE instead of XXX

GHOST_ContextEGL.cpp: It really does seem that it is not possible to query the swap interval using EGL

GHOST_WidnowCocoa.h: The comment referring to Carbon is clearly out of date, so I removed it.

math_geom.c: The node about not using tmax again is correct, but the code is kept for a future maintainer who will need to know how to compute it if they modify that code.

paint_image_proj.c (2698): The question about integer truncation does not appear to have been resolved.  It still seems to be an incorrectly implementation of rounding (I'd suggest using the round function instead of this hack).
2014-10-22 20:03:25 -05:00

641 lines
17 KiB
C++

/*
* ***** 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)
{
// This is a bit of a kludge because there does not seem to
// be a way to query the swap interval with EGL.
intervalOut = m_swap_interval;
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;
}