1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ui/gl/gl_context_egl.h"
6
7#include "base/debug/trace_event.h"
8#include "base/logging.h"
9#include "base/memory/scoped_ptr.h"
10#include "build/build_config.h"
11#include "third_party/khronos/EGL/egl.h"
12#include "third_party/khronos/EGL/eglext.h"
13#include "ui/gl/egl_util.h"
14#include "ui/gl/gl_bindings.h"
15#include "ui/gl/gl_surface_egl.h"
16
17#if defined(USE_X11)
18extern "C" {
19#include <X11/Xlib.h>
20}
21#endif
22
23using ui::GetLastEGLErrorString;
24
25namespace gfx {
26
27GLContextEGL::GLContextEGL(GLShareGroup* share_group)
28    : GLContextReal(share_group),
29      context_(NULL),
30      display_(NULL),
31      config_(NULL),
32      unbind_fbo_on_makecurrent_(false) {
33}
34
35bool GLContextEGL::Initialize(
36    GLSurface* compatible_surface, GpuPreference gpu_preference) {
37  DCHECK(compatible_surface);
38  DCHECK(!context_);
39
40  static const EGLint kContextAttributes[] = {
41    EGL_CONTEXT_CLIENT_VERSION, 2,
42    EGL_NONE
43  };
44  static const EGLint kContextRobustnessAttributes[] = {
45    EGL_CONTEXT_CLIENT_VERSION, 2,
46    EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT,
47    EGL_LOSE_CONTEXT_ON_RESET_EXT,
48    EGL_NONE
49  };
50
51  display_ = compatible_surface->GetDisplay();
52  config_ = compatible_surface->GetConfig();
53
54  const EGLint* context_attributes = NULL;
55  if (GLSurfaceEGL::IsCreateContextRobustnessSupported()) {
56    DVLOG(1) << "EGL_EXT_create_context_robustness supported.";
57    context_attributes = kContextRobustnessAttributes;
58  } else {
59    // At some point we should require the presence of the robustness
60    // extension and remove this code path.
61    DVLOG(1) << "EGL_EXT_create_context_robustness NOT supported.";
62    context_attributes = kContextAttributes;
63  }
64
65  context_ = eglCreateContext(
66      display_,
67      config_,
68      share_group() ? share_group()->GetHandle() : NULL,
69      context_attributes);
70
71  if (!context_) {
72    LOG(ERROR) << "eglCreateContext failed with error "
73               << GetLastEGLErrorString();
74    return false;
75  }
76
77  return true;
78}
79
80void GLContextEGL::Destroy() {
81  if (context_) {
82    if (!eglDestroyContext(display_, context_)) {
83      LOG(ERROR) << "eglDestroyContext failed with error "
84                 << GetLastEGLErrorString();
85    }
86
87    context_ = NULL;
88  }
89}
90
91bool GLContextEGL::MakeCurrent(GLSurface* surface) {
92  DCHECK(context_);
93  if (IsCurrent(surface))
94      return true;
95
96  ScopedReleaseCurrent release_current;
97  TRACE_EVENT2("gpu", "GLContextEGL::MakeCurrent",
98               "context", context_,
99               "surface", surface);
100
101  if (unbind_fbo_on_makecurrent_ &&
102      eglGetCurrentContext() != EGL_NO_CONTEXT) {
103    glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
104  }
105
106  if (!eglMakeCurrent(display_,
107                      surface->GetHandle(),
108                      surface->GetHandle(),
109                      context_)) {
110    DVLOG(1) << "eglMakeCurrent failed with error "
111             << GetLastEGLErrorString();
112    return false;
113  }
114
115  // Set this as soon as the context is current, since we might call into GL.
116  SetRealGLApi();
117
118  SetCurrent(surface);
119  if (!InitializeDynamicBindings()) {
120    return false;
121  }
122
123  if (!surface->OnMakeCurrent(this)) {
124    LOG(ERROR) << "Could not make current.";
125    return false;
126  }
127
128  release_current.Cancel();
129  return true;
130}
131
132void GLContextEGL::SetUnbindFboOnMakeCurrent() {
133  unbind_fbo_on_makecurrent_ = true;
134}
135
136void GLContextEGL::ReleaseCurrent(GLSurface* surface) {
137  if (!IsCurrent(surface))
138    return;
139
140  if (unbind_fbo_on_makecurrent_)
141    glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
142
143  SetCurrent(NULL);
144  eglMakeCurrent(display_,
145                 EGL_NO_SURFACE,
146                 EGL_NO_SURFACE,
147                 EGL_NO_CONTEXT);
148}
149
150bool GLContextEGL::IsCurrent(GLSurface* surface) {
151  DCHECK(context_);
152
153  bool native_context_is_current = context_ == eglGetCurrentContext();
154
155  // If our context is current then our notion of which GLContext is
156  // current must be correct. On the other hand, third-party code
157  // using OpenGL might change the current context.
158  DCHECK(!native_context_is_current || (GetRealCurrent() == this));
159
160  if (!native_context_is_current)
161    return false;
162
163  if (surface) {
164    if (surface->GetHandle() != eglGetCurrentSurface(EGL_DRAW))
165      return false;
166  }
167
168  return true;
169}
170
171void* GLContextEGL::GetHandle() {
172  return context_;
173}
174
175void GLContextEGL::SetSwapInterval(int interval) {
176  DCHECK(IsCurrent(NULL));
177  if (!eglSwapInterval(display_, interval)) {
178    LOG(ERROR) << "eglSwapInterval failed with error "
179               << GetLastEGLErrorString();
180  }
181}
182
183std::string GLContextEGL::GetExtensions() {
184  const char* extensions = eglQueryString(display_,
185                                          EGL_EXTENSIONS);
186  if (!extensions)
187    return GLContext::GetExtensions();
188
189  return GLContext::GetExtensions() + " " + extensions;
190}
191
192bool GLContextEGL::WasAllocatedUsingRobustnessExtension() {
193  return GLSurfaceEGL::IsCreateContextRobustnessSupported();
194}
195
196GLContextEGL::~GLContextEGL() {
197  Destroy();
198}
199
200#if !defined(OS_ANDROID)
201bool GLContextEGL::GetTotalGpuMemory(size_t* bytes) {
202  DCHECK(bytes);
203  *bytes = 0;
204  return false;
205}
206#endif
207
208}  // namespace gfx
209