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
5extern "C" {
6#include <X11/Xlib.h>
7}
8
9#include "ui/gl/gl_context_glx.h"
10
11#include "base/debug/trace_event.h"
12#include "base/logging.h"
13#include "base/memory/scoped_ptr.h"
14#include "ui/gl/GL/glextchromium.h"
15#include "ui/gl/gl_bindings.h"
16#include "ui/gl/gl_implementation.h"
17#include "ui/gl/gl_surface_glx.h"
18
19namespace gfx {
20
21GLContextGLX::GLContextGLX(GLShareGroup* share_group)
22  : GLContextReal(share_group),
23    context_(NULL),
24    display_(NULL) {
25}
26
27XDisplay* GLContextGLX::display() {
28  return display_;
29}
30
31bool GLContextGLX::Initialize(
32    GLSurface* compatible_surface, GpuPreference gpu_preference) {
33  display_ = static_cast<XDisplay*>(compatible_surface->GetDisplay());
34
35  GLXContext share_handle = static_cast<GLXContext>(
36      share_group() ? share_group()->GetHandle() : NULL);
37
38  if (GLSurfaceGLX::IsCreateContextSupported()) {
39    DVLOG(1) << "GLX_ARB_create_context supported.";
40    std::vector<int> attribs;
41    if (GLSurfaceGLX::IsCreateContextRobustnessSupported()) {
42      DVLOG(1) << "GLX_ARB_create_context_robustness supported.";
43      attribs.push_back(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB);
44      attribs.push_back(GLX_LOSE_CONTEXT_ON_RESET_ARB);
45    }
46    attribs.push_back(0);
47    context_ = glXCreateContextAttribsARB(
48        display_,
49        static_cast<GLXFBConfig>(compatible_surface->GetConfig()),
50        share_handle,
51        True,
52        &attribs.front());
53    if (!context_) {
54      LOG(ERROR) << "Failed to create GL context with "
55                 << "glXCreateContextAttribsARB.";
56      return false;
57    }
58  } else {
59    DVLOG(1) << "GLX_ARB_create_context not supported.";
60    context_ = glXCreateNewContext(
61       display_,
62       static_cast<GLXFBConfig>(compatible_surface->GetConfig()),
63       GLX_RGBA_TYPE,
64       share_handle,
65       True);
66    if (!context_) {
67      LOG(ERROR) << "Failed to create GL context with glXCreateNewContext.";
68      return false;
69    }
70  }
71  DCHECK(context_);
72  DVLOG(1) << "  Successfully allocated "
73           << (compatible_surface->IsOffscreen() ?
74               "offscreen" : "onscreen")
75           << " GL context with LOSE_CONTEXT_ON_RESET_ARB";
76
77  DVLOG(1) << (compatible_surface->IsOffscreen() ? "Offscreen" : "Onscreen")
78           << " context was "
79           << (glXIsDirect(display_,
80                           static_cast<GLXContext>(context_))
81                   ? "direct" : "indirect")
82           << ".";
83
84  return true;
85}
86
87void GLContextGLX::Destroy() {
88  if (context_) {
89    glXDestroyContext(display_,
90                      static_cast<GLXContext>(context_));
91    context_ = NULL;
92  }
93}
94
95bool GLContextGLX::MakeCurrent(GLSurface* surface) {
96  DCHECK(context_);
97  if (IsCurrent(surface))
98    return true;
99
100  ScopedReleaseCurrent release_current;
101  TRACE_EVENT0("gpu", "GLContextGLX::MakeCurrent");
102  if (!glXMakeContextCurrent(
103      display_,
104      reinterpret_cast<GLXDrawable>(surface->GetHandle()),
105      reinterpret_cast<GLXDrawable>(surface->GetHandle()),
106      static_cast<GLXContext>(context_))) {
107    LOG(ERROR) << "Couldn't make context current with X drawable.";
108    Destroy();
109    return false;
110  }
111
112  // Set this as soon as the context is current, since we might call into GL.
113  SetRealGLApi();
114
115  SetCurrent(surface);
116  if (!InitializeDynamicBindings()) {
117    Destroy();
118    return false;
119  }
120
121  if (!surface->OnMakeCurrent(this)) {
122    LOG(ERROR) << "Could not make current.";
123    Destroy();
124    return false;
125  }
126
127  release_current.Cancel();
128  return true;
129}
130
131void GLContextGLX::ReleaseCurrent(GLSurface* surface) {
132  if (!IsCurrent(surface))
133    return;
134
135  SetCurrent(NULL);
136  if (!glXMakeContextCurrent(display_, 0, 0, 0))
137    LOG(ERROR) << "glXMakeCurrent failed in ReleaseCurrent";
138}
139
140bool GLContextGLX::IsCurrent(GLSurface* surface) {
141  bool native_context_is_current =
142      glXGetCurrentContext() == static_cast<GLXContext>(context_);
143
144  // If our context is current then our notion of which GLContext is
145  // current must be correct. On the other hand, third-party code
146  // using OpenGL might change the current context.
147  DCHECK(!native_context_is_current || (GetRealCurrent() == this));
148
149  if (!native_context_is_current)
150    return false;
151
152  if (surface) {
153    if (glXGetCurrentDrawable() !=
154        reinterpret_cast<GLXDrawable>(surface->GetHandle())) {
155      return false;
156    }
157  }
158
159  return true;
160}
161
162void* GLContextGLX::GetHandle() {
163  return context_;
164}
165
166void GLContextGLX::SetSwapInterval(int interval) {
167  DCHECK(IsCurrent(NULL));
168  if (HasExtension("GLX_EXT_swap_control") &&
169      g_driver_glx.fn.glXSwapIntervalEXTFn) {
170    glXSwapIntervalEXT(
171        display_,
172        glXGetCurrentDrawable(),
173        interval);
174  } else if (HasExtension("GLX_MESA_swap_control") &&
175             g_driver_glx.fn.glXSwapIntervalMESAFn) {
176    glXSwapIntervalMESA(interval);
177  } else {
178    if(interval == 0)
179      LOG(WARNING) <<
180          "Could not disable vsync: driver does not "
181          "support GLX_EXT_swap_control";
182  }
183}
184
185std::string GLContextGLX::GetExtensions() {
186  DCHECK(IsCurrent(NULL));
187  const char* extensions = GLSurfaceGLX::GetGLXExtensions();
188  if (extensions) {
189    return GLContext::GetExtensions() + " " + extensions;
190  }
191
192  return GLContext::GetExtensions();
193}
194
195bool GLContextGLX::GetTotalGpuMemory(size_t* bytes) {
196  DCHECK(bytes);
197  *bytes = 0;
198  if (HasExtension("GL_NVX_gpu_memory_info")) {
199    GLint kbytes = 0;
200    glGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &kbytes);
201    *bytes = 1024*kbytes;
202    return true;
203  }
204  return false;
205}
206
207bool GLContextGLX::WasAllocatedUsingRobustnessExtension() {
208  return GLSurfaceGLX::IsCreateContextRobustnessSupported();
209}
210
211GLContextGLX::~GLContextGLX() {
212  Destroy();
213}
214
215}  // namespace gfx
216