blob: f36a2bdc8c06ce19c5a0e2f111dadd02f902ac0d [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.
extern "C" {
#include <X11/Xlib.h>
}
#include "ui/gl/gl_context_glx.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "third_party/mesa/MesaLib/include/GL/osmesa.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_surface_glx.h"
namespace gfx {
namespace {
// scoped_ptr functor for XFree(). Use as follows:
// scoped_ptr_malloc<XVisualInfo, ScopedPtrXFree> foo(...);
// where "XVisualInfo" is any X type that is freed with XFree.
class ScopedPtrXFree {
public:
void operator()(void* x) const {
::XFree(x);
}
};
} // namespace
GLContextGLX::GLContextGLX(GLShareGroup* share_group)
: GLContext(share_group),
context_(NULL),
display_(NULL) {
}
Display* GLContextGLX::display() {
return display_;
}
bool GLContextGLX::Initialize(
GLSurface* compatible_surface, GpuPreference gpu_preference) {
display_ = static_cast<Display*>(compatible_surface->GetDisplay());
GLXContext share_handle = static_cast<GLXContext>(
share_group() ? share_group()->GetHandle() : NULL);
if (GLSurfaceGLX::IsCreateContextRobustnessSupported()) {
DVLOG(1) << "GLX_ARB_create_context_robustness supported.";
std::vector<int> attribs;
attribs.push_back(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB);
attribs.push_back(GLX_LOSE_CONTEXT_ON_RESET_ARB);
attribs.push_back(0);
context_ = glXCreateContextAttribsARB(
display_,
static_cast<GLXFBConfig>(compatible_surface->GetConfig()),
share_handle,
True,
&attribs.front());
if (context_) {
DVLOG(1) << " Successfully allocated "
<< (compatible_surface->IsOffscreen() ?
"offscreen" : "onscreen")
<< " GL context with LOSE_CONTEXT_ON_RESET_ARB";
} else {
// TODO(kbr): it is not expected that things will work properly
// in this case, since we will likely allocate our offscreen
// contexts with this bit set and the onscreen contexts without,
// and won't be able to put them in the same share group.
// Consider what to do here; force loss of all contexts and
// reallocation without ARB_robustness?
LOG(ERROR) <<
" FAILED to allocate GL context with LOSE_CONTEXT_ON_RESET_ARB";
}
}
if (!context_) {
// The means by which the context is created depends on whether
// the drawable type works reliably with GLX 1.3. If it does not
// then fall back to GLX 1.2.
if (compatible_surface->IsOffscreen()) {
context_ = glXCreateNewContext(
display_,
static_cast<GLXFBConfig>(compatible_surface->GetConfig()),
GLX_RGBA_TYPE,
share_handle,
True);
} else {
// Get the visuals for the X drawable.
XWindowAttributes attributes;
if (!XGetWindowAttributes(
display_,
reinterpret_cast<GLXDrawable>(compatible_surface->GetHandle()),
&attributes)) {
LOG(ERROR) << "XGetWindowAttributes failed for window " <<
reinterpret_cast<GLXDrawable>(
compatible_surface->GetHandle()) << ".";
return false;
}
XVisualInfo visual_info_template;
visual_info_template.visualid = XVisualIDFromVisual(attributes.visual);
int visual_info_count = 0;
scoped_ptr_malloc<XVisualInfo, ScopedPtrXFree> visual_info_list(
XGetVisualInfo(display_, VisualIDMask,
&visual_info_template,
&visual_info_count));
DCHECK(visual_info_list.get());
if (visual_info_count == 0) {
LOG(ERROR) << "No visual info for visual ID.";
return false;
}
// Attempt to create a context with each visual in turn until one works.
context_ = glXCreateContext(
display_,
visual_info_list.get(),
share_handle,
True);
}
}
if (!context_) {
LOG(ERROR) << "Couldn't create GL context.";
return false;
}
DVLOG(1) << (compatible_surface->IsOffscreen() ? "Offscreen" : "Onscreen")
<< " context was "
<< (glXIsDirect(display_,
static_cast<GLXContext>(context_))
? "direct" : "indirect")
<< ".";
return true;
}
void GLContextGLX::Destroy() {
if (context_) {
glXDestroyContext(display_,
static_cast<GLXContext>(context_));
context_ = NULL;
}
}
bool GLContextGLX::MakeCurrent(GLSurface* surface) {
DCHECK(context_);
if (IsCurrent(surface))
return true;
TRACE_EVENT0("gpu", "GLContextGLX::MakeCurrent");
if (!glXMakeCurrent(
display_,
reinterpret_cast<GLXDrawable>(surface->GetHandle()),
static_cast<GLXContext>(context_))) {
LOG(ERROR) << "Couldn't make context current with X drawable.";
Destroy();
return false;
}
SetCurrent(this, surface);
if (!InitializeExtensionBindings()) {
ReleaseCurrent(surface);
Destroy();
return false;
}
if (!surface->OnMakeCurrent(this)) {
LOG(ERROR) << "Could not make current.";
ReleaseCurrent(surface);
Destroy();
return false;
}
SetRealGLApi();
return true;
}
void GLContextGLX::ReleaseCurrent(GLSurface* surface) {
if (!IsCurrent(surface))
return;
SetCurrent(NULL, NULL);
if (!glXMakeCurrent(display_, 0, 0))
LOG(ERROR) << "glXMakeCurrent failed in ReleaseCurrent";
}
bool GLContextGLX::IsCurrent(GLSurface* surface) {
bool native_context_is_current =
glXGetCurrentContext() == static_cast<GLXContext>(context_);
// 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 || (GetCurrent() == this));
if (!native_context_is_current)
return false;
if (surface) {
if (glXGetCurrentDrawable() !=
reinterpret_cast<GLXDrawable>(surface->GetHandle())) {
return false;
}
}
return true;
}
void* GLContextGLX::GetHandle() {
return context_;
}
void GLContextGLX::SetSwapInterval(int interval) {
DCHECK(IsCurrent(NULL));
if (HasExtension("GLX_EXT_swap_control") &&
g_driver_glx.fn.glXSwapIntervalEXTFn) {
glXSwapIntervalEXT(
display_,
glXGetCurrentDrawable(),
interval);
} else if (HasExtension("GLX_MESA_swap_control") &&
g_driver_glx.fn.glXSwapIntervalMESAFn) {
glXSwapIntervalMESA(interval);
} else {
if(interval == 0)
LOG(WARNING) <<
"Could not disable vsync: driver does not "
"support GLX_EXT_swap_control";
}
}
std::string GLContextGLX::GetExtensions() {
DCHECK(IsCurrent(NULL));
const char* extensions = GLSurfaceGLX::GetGLXExtensions();
if (extensions) {
return GLContext::GetExtensions() + " " + extensions;
}
return GLContext::GetExtensions();
}
bool GLContextGLX::GetTotalGpuMemory(size_t* bytes) {
DCHECK(bytes);
*bytes = 0;
if (HasExtension("GL_NVX_gpu_memory_info")) {
GLint kbytes = 0;
glGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &kbytes);
*bytes = 1024*kbytes;
return true;
}
return false;
}
bool GLContextGLX::WasAllocatedUsingRobustnessExtension() {
return GLSurfaceGLX::IsCreateContextRobustnessSupported();
}
GLContextGLX::~GLContextGLX() {
Destroy();
}
} // namespace gfx