blob: 98ac5cf50419f9462652e957ce45c8b90b8a55b9 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/gl/gl_context_egl.h"
#include <memory>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "third_party/khronos/EGL/egl.h"
#include "third_party/khronos/EGL/eglext.h"
#include "ui/gl/egl_util.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_surface_egl.h"
#if defined(USE_X11)
extern "C" {
#include <X11/Xlib.h>
}
#endif
using ui::GetLastEGLErrorString;
namespace gl {
GLContextEGL::GLContextEGL(GLShareGroup* share_group)
: GLContextReal(share_group),
context_(nullptr),
display_(nullptr),
config_(nullptr),
unbind_fbo_on_makecurrent_(false),
swap_interval_(1) {
}
bool GLContextEGL::Initialize(
GLSurface* compatible_surface, GpuPreference gpu_preference) {
DCHECK(compatible_surface);
DCHECK(!context_);
EGLint context_client_version = 2;
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableUnsafeES3APIs)) {
context_client_version = 3;
}
const EGLint kContextAttributes[] = {
EGL_CONTEXT_CLIENT_VERSION, context_client_version,
EGL_NONE
};
const EGLint kContextRobustnessAttributes[] = {
EGL_CONTEXT_CLIENT_VERSION, context_client_version,
EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT,
EGL_LOSE_CONTEXT_ON_RESET_EXT,
EGL_NONE
};
display_ = compatible_surface->GetDisplay();
config_ = compatible_surface->GetConfig();
const EGLint* context_attributes = nullptr;
if (GLSurfaceEGL::IsCreateContextRobustnessSupported()) {
DVLOG(1) << "EGL_EXT_create_context_robustness supported.";
context_attributes = kContextRobustnessAttributes;
} else {
// At some point we should require the presence of the robustness
// extension and remove this code path.
DVLOG(1) << "EGL_EXT_create_context_robustness NOT supported.";
context_attributes = kContextAttributes;
}
if (!eglBindAPI(EGL_OPENGL_ES_API)) {
LOG(ERROR) << "eglBindApi failed with error "
<< GetLastEGLErrorString();
return false;
}
context_ = eglCreateContext(
display_,
config_,
share_group() ? share_group()->GetHandle() : nullptr,
context_attributes);
if (!context_) {
LOG(ERROR) << "eglCreateContext failed with error "
<< GetLastEGLErrorString();
return false;
}
return true;
}
void GLContextEGL::Destroy() {
if (context_) {
if (!eglDestroyContext(display_, context_)) {
LOG(ERROR) << "eglDestroyContext failed with error "
<< GetLastEGLErrorString();
}
context_ = nullptr;
}
}
bool GLContextEGL::MakeCurrent(GLSurface* surface) {
DCHECK(context_);
if (IsCurrent(surface))
return true;
ScopedReleaseCurrent release_current;
TRACE_EVENT2("gpu", "GLContextEGL::MakeCurrent",
"context", context_,
"surface", surface);
if (unbind_fbo_on_makecurrent_ &&
eglGetCurrentContext() != EGL_NO_CONTEXT) {
glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
}
if (!eglMakeCurrent(display_,
surface->GetHandle(),
surface->GetHandle(),
context_)) {
DVLOG(1) << "eglMakeCurrent failed with error "
<< GetLastEGLErrorString();
return false;
}
// Set this as soon as the context is current, since we might call into GL.
SetRealGLApi();
SetCurrent(surface);
if (!InitializeDynamicBindings()) {
return false;
}
if (!surface->OnMakeCurrent(this)) {
LOG(ERROR) << "Could not make current.";
return false;
}
surface->OnSetSwapInterval(swap_interval_);
release_current.Cancel();
return true;
}
void GLContextEGL::SetUnbindFboOnMakeCurrent() {
unbind_fbo_on_makecurrent_ = true;
}
void GLContextEGL::ReleaseCurrent(GLSurface* surface) {
if (!IsCurrent(surface))
return;
if (unbind_fbo_on_makecurrent_)
glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
SetCurrent(nullptr);
eglMakeCurrent(display_,
EGL_NO_SURFACE,
EGL_NO_SURFACE,
EGL_NO_CONTEXT);
}
bool GLContextEGL::IsCurrent(GLSurface* surface) {
DCHECK(context_);
bool native_context_is_current = context_ == eglGetCurrentContext();
// If our context is current then our notion of which GLContext is
// current must be correct. On the other hand, third-party code
// using OpenGL might change the current context.
DCHECK(!native_context_is_current || (GetRealCurrent() == this));
if (!native_context_is_current)
return false;
if (surface) {
if (surface->GetHandle() != eglGetCurrentSurface(EGL_DRAW))
return false;
}
return true;
}
void* GLContextEGL::GetHandle() {
return context_;
}
void GLContextEGL::OnSetSwapInterval(int interval) {
DCHECK(IsCurrent(nullptr) && GLSurface::GetCurrent());
// This is a surfaceless context. eglSwapInterval doesn't take any effect in
// this case and will just return EGL_BAD_SURFACE.
if (GLSurface::GetCurrent()->IsSurfaceless())
return;
if (!eglSwapInterval(display_, interval)) {
LOG(ERROR) << "eglSwapInterval failed with error "
<< GetLastEGLErrorString();
} else {
swap_interval_ = interval;
GLSurface::GetCurrent()->OnSetSwapInterval(interval);
}
}
std::string GLContextEGL::GetExtensions() {
const char* extensions = eglQueryString(display_,
EGL_EXTENSIONS);
if (!extensions)
return GLContext::GetExtensions();
return GLContext::GetExtensions() + " " + extensions;
}
bool GLContextEGL::WasAllocatedUsingRobustnessExtension() {
return GLSurfaceEGL::IsCreateContextRobustnessSupported();
}
GLContextEGL::~GLContextEGL() {
Destroy();
}
} // namespace gl