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  TRACE_EVENT2("gpu", "GLContextEGL::MakeCurrent",
97               "context", context_,
98               "surface", surface);
99
100  if (unbind_fbo_on_makecurrent_ &&
101      eglGetCurrentContext() != EGL_NO_CONTEXT) {
102    glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
103  }
104
105  if (!eglMakeCurrent(display_,
106                      surface->GetHandle(),
107                      surface->GetHandle(),
108                      context_)) {
109    DVLOG(1) << "eglMakeCurrent failed with error "
110             << GetLastEGLErrorString();
111    return false;
112  }
113
114  // Set this as soon as the context is current, since we might call into GL.
115  SetRealGLApi();
116
117  SetCurrent(surface);
118  if (!InitializeExtensionBindings()) {
119    ReleaseCurrent(surface);
120    return false;
121  }
122
123  if (!surface->OnMakeCurrent(this)) {
124    LOG(ERROR) << "Could not make current.";
125    return false;
126  }
127
128  return true;
129}
130
131void GLContextEGL::SetUnbindFboOnMakeCurrent() {
132  unbind_fbo_on_makecurrent_ = true;
133}
134
135void GLContextEGL::ReleaseCurrent(GLSurface* surface) {
136  if (!IsCurrent(surface))
137    return;
138
139  if (unbind_fbo_on_makecurrent_)
140    glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
141
142  SetCurrent(NULL);
143  eglMakeCurrent(display_,
144                 EGL_NO_SURFACE,
145                 EGL_NO_SURFACE,
146                 EGL_NO_CONTEXT);
147}
148
149bool GLContextEGL::IsCurrent(GLSurface* surface) {
150  DCHECK(context_);
151
152  bool native_context_is_current = context_ == eglGetCurrentContext();
153
154  // If our context is current then our notion of which GLContext is
155  // current must be correct. On the other hand, third-party code
156  // using OpenGL might change the current context.
157  DCHECK(!native_context_is_current || (GetRealCurrent() == this));
158
159  if (!native_context_is_current)
160    return false;
161
162  if (surface) {
163    if (surface->GetHandle() != eglGetCurrentSurface(EGL_DRAW))
164      return false;
165  }
166
167  return true;
168}
169
170void* GLContextEGL::GetHandle() {
171  return context_;
172}
173
174void GLContextEGL::SetSwapInterval(int interval) {
175  DCHECK(IsCurrent(NULL));
176  if (!eglSwapInterval(display_, interval)) {
177    LOG(ERROR) << "eglSwapInterval failed with error "
178               << GetLastEGLErrorString();
179  }
180}
181
182std::string GLContextEGL::GetExtensions() {
183  const char* extensions = eglQueryString(display_,
184                                          EGL_EXTENSIONS);
185  if (!extensions)
186    return GLContext::GetExtensions();
187
188  return GLContext::GetExtensions() + " " + extensions;
189}
190
191bool GLContextEGL::WasAllocatedUsingRobustnessExtension() {
192  return GLSurfaceEGL::IsCreateContextRobustnessSupported();
193}
194
195GLContextEGL::~GLContextEGL() {
196  Destroy();
197}
198
199#if !defined(OS_ANDROID)
200bool GLContextEGL::GetTotalGpuMemory(size_t* bytes) {
201  DCHECK(bytes);
202  *bytes = 0;
203  return false;
204}
205#endif
206
207}  // namespace gfx
208