summaryrefslogtreecommitdiffstats
path: root/app/gfx/gl/gl_context_linux.cc
diff options
context:
space:
mode:
Diffstat (limited to 'app/gfx/gl/gl_context_linux.cc')
-rw-r--r--app/gfx/gl/gl_context_linux.cc402
1 files changed, 402 insertions, 0 deletions
diff --git a/app/gfx/gl/gl_context_linux.cc b/app/gfx/gl/gl_context_linux.cc
new file mode 100644
index 0000000..a9995d65
--- /dev/null
+++ b/app/gfx/gl/gl_context_linux.cc
@@ -0,0 +1,402 @@
+// Copyright (c) 2010 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.
+
+// This file implements the ViewGLContext and PbufferGLContext classes.
+
+#include <dlfcn.h>
+#include <GL/glew.h>
+#include <GL/glxew.h>
+#include <GL/glx.h>
+#include <GL/osmew.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include "app/x11_util.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "app/gfx/gl/gl_context.h"
+#include "app/gfx/gl/gl_context_osmesa.h"
+
+namespace gfx {
+
+typedef GLXContext GLContextHandle;
+typedef GLXPbuffer PbufferHandle;
+
+// This class is a wrapper around a GL context that renders directly to a
+// window.
+class ViewGLContext : public GLContext {
+ public:
+ explicit ViewGLContext(gfx::PluginWindowHandle window)
+ : window_(window),
+ context_(NULL) {
+ DCHECK(window);
+ }
+
+ // Initializes the GL context.
+ bool Initialize(bool multisampled);
+
+ virtual void Destroy();
+ virtual bool MakeCurrent();
+ virtual bool IsCurrent();
+ virtual bool IsOffscreen();
+ virtual void SwapBuffers();
+ virtual gfx::Size GetSize();
+ virtual void* GetHandle();
+
+ private:
+ gfx::PluginWindowHandle window_;
+ GLContextHandle context_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewGLContext);
+};
+
+// This class is a wrapper around a GL context used for offscreen rendering.
+// It is initially backed by a 1x1 pbuffer. Use it to create an FBO to do useful
+// rendering.
+class PbufferGLContext : public GLContext {
+ public:
+ explicit PbufferGLContext()
+ : context_(NULL),
+ pbuffer_(NULL) {
+ }
+
+ // Initializes the GL context.
+ bool Initialize(void* shared_handle);
+
+ virtual void Destroy();
+ virtual bool MakeCurrent();
+ virtual bool IsCurrent();
+ virtual bool IsOffscreen();
+ virtual void SwapBuffers();
+ virtual gfx::Size GetSize();
+ virtual void* GetHandle();
+
+ private:
+ GLContextHandle context_;
+ PbufferHandle pbuffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(PbufferGLContext);
+};
+
+// 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);
+ }
+};
+
+// Some versions of NVIDIA's GL libGL.so include a broken version of
+// dlopen/dlsym, and so linking it into chrome breaks it. So we dynamically
+// load it, and use glew to dynamically resolve symbols.
+// See http://code.google.com/p/chromium/issues/detail?id=16800
+
+static bool InitializeOneOff() {
+ static bool initialized = false;
+ if (initialized)
+ return true;
+
+ osmewInit();
+ if (!OSMesaCreateContext) {
+ void* handle = dlopen("libGL.so.1", RTLD_LAZY | RTLD_GLOBAL);
+ if (!handle) {
+ LOG(ERROR) << "Could not find libGL.so.1";
+ return false;
+ }
+
+ // Initializes context-independent parts of GLEW
+ if (glxewInit() != GLEW_OK) {
+ LOG(ERROR) << "glxewInit failed";
+ return false;
+ }
+ // glxewContextInit really only needs a display connection to
+ // complete, and we don't want to have to create an OpenGL context
+ // just to get access to GLX 1.3 entry points to create pbuffers.
+ // We therefore added a glxewContextInitWithDisplay entry point.
+ Display* display = x11_util::GetXDisplay();
+ if (glxewContextInitWithDisplay(display) != GLEW_OK) {
+ LOG(ERROR) << "glxewContextInit failed";
+ return false;
+ }
+ }
+
+ initialized = true;
+ return true;
+}
+
+bool ViewGLContext::Initialize(bool multisampled) {
+ if (multisampled) {
+ DLOG(WARNING) << "Multisampling not implemented.";
+ }
+
+ Display* display = x11_util::GetXDisplay();
+ XWindowAttributes attributes;
+ XGetWindowAttributes(display, window_, &attributes);
+ 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());
+ DCHECK_GT(visual_info_count, 0);
+ context_ = NULL;
+ for (int i = 0; i < visual_info_count; ++i) {
+ context_ = glXCreateContext(display, visual_info_list.get() + i, 0, True);
+ if (context_)
+ break;
+ }
+ if (!context_) {
+ DLOG(ERROR) << "Couldn't create GL context.";
+ return false;
+ }
+
+ if (!MakeCurrent()) {
+ Destroy();
+ DLOG(ERROR) << "Couldn't make context current for initialization.";
+ return false;
+ }
+
+ if (!InitializeGLEW()) {
+ Destroy();
+ return false;
+ }
+
+ if (!InitializeCommon()) {
+ Destroy();
+ return false;
+ }
+
+ return true;
+}
+
+void ViewGLContext::Destroy() {
+ Display* display = x11_util::GetXDisplay();
+ Bool result = glXMakeCurrent(display, 0, 0);
+
+ // glXMakeCurrent isn't supposed to fail when unsetting the context, unless
+ // we have pending draws on an invalid window - which shouldn't be the case
+ // here.
+ DCHECK(result);
+ if (context_) {
+ glXDestroyContext(display, context_);
+ context_ = NULL;
+ }
+}
+
+bool ViewGLContext::MakeCurrent() {
+ if (IsCurrent()) {
+ return true;
+ }
+
+ Display* display = x11_util::GetXDisplay();
+ if (glXMakeCurrent(display, window_, context_) != True) {
+ glXDestroyContext(display, context_);
+ context_ = 0;
+ DLOG(ERROR) << "Couldn't make context current.";
+ return false;
+ }
+
+ return true;
+}
+
+bool ViewGLContext::IsCurrent() {
+ return glXGetCurrentDrawable() == window_ &&
+ glXGetCurrentContext() == context_;
+}
+
+bool ViewGLContext::IsOffscreen() {
+ return false;
+}
+
+void ViewGLContext::SwapBuffers() {
+ Display* display = x11_util::GetXDisplay();
+ glXSwapBuffers(display, window_);
+}
+
+gfx::Size ViewGLContext::GetSize() {
+ XWindowAttributes attributes;
+ Display* display = x11_util::GetXDisplay();
+ XGetWindowAttributes(display, window_, &attributes);
+ return gfx::Size(attributes.width, attributes.height);
+}
+
+void* ViewGLContext::GetHandle() {
+ return context_;
+}
+
+GLContext* GLContext::CreateViewGLContext(gfx::PluginWindowHandle window,
+ bool multisampled) {
+ if (!InitializeOneOff())
+ return NULL;
+
+ if (OSMesaCreateContext) {
+ // TODO(apatrick): Support OSMesa rendering to a window on Linux.
+ NOTREACHED() << "OSMesa rendering to a window is not yet implemented.";
+ return NULL;
+ } else {
+ scoped_ptr<ViewGLContext> context(new ViewGLContext(window));
+
+ if (!context->Initialize(multisampled))
+ return NULL;
+
+ return context.release();
+ }
+}
+
+bool PbufferGLContext::Initialize(void* shared_handle) {
+ if (!glXChooseFBConfig ||
+ !glXCreateNewContext ||
+ !glXCreatePbuffer ||
+ !glXDestroyPbuffer) {
+ DLOG(ERROR) << "Pbuffer support not available.";
+ return false;
+ }
+
+ static const int config_attributes[] = {
+ GLX_DRAWABLE_TYPE,
+ GLX_PBUFFER_BIT,
+ GLX_RENDER_TYPE,
+ GLX_RGBA_BIT,
+ GLX_DOUBLEBUFFER,
+ 0,
+ 0
+ };
+
+ Display* display = x11_util::GetXDisplay();
+
+ int nelements = 0;
+ // TODO(kbr): figure out whether hardcoding screen to 0 is sufficient.
+ scoped_ptr_malloc<GLXFBConfig, ScopedPtrXFree> config(
+ glXChooseFBConfig(display, 0, config_attributes, &nelements));
+ if (!config.get()) {
+ DLOG(ERROR) << "glXChooseFBConfig failed.";
+ return false;
+ }
+ if (!nelements) {
+ DLOG(ERROR) << "glXChooseFBConfig returned 0 elements.";
+ return false;
+ }
+ context_ = glXCreateNewContext(display,
+ config.get()[0],
+ GLX_RGBA_TYPE,
+ static_cast<GLContextHandle>(shared_handle),
+ True);
+ if (!context_) {
+ DLOG(ERROR) << "glXCreateNewContext failed.";
+ return false;
+ }
+ static const int pbuffer_attributes[] = {
+ GLX_PBUFFER_WIDTH,
+ 1,
+ GLX_PBUFFER_HEIGHT,
+ 1,
+ 0
+ };
+ pbuffer_ = glXCreatePbuffer(display,
+ config.get()[0], pbuffer_attributes);
+ if (!pbuffer_) {
+ Destroy();
+ DLOG(ERROR) << "glXCreatePbuffer failed.";
+ return false;
+ }
+
+ if (!MakeCurrent()) {
+ Destroy();
+ DLOG(ERROR) << "Couldn't make context current for initialization.";
+ return false;
+ }
+
+ if (!InitializeGLEW()) {
+ Destroy();
+ return false;
+ }
+
+ if (!InitializeCommon()) {
+ Destroy();
+ return false;
+ }
+
+ return true;
+}
+
+void PbufferGLContext::Destroy() {
+ Display* display = x11_util::GetXDisplay();
+ Bool result = glXMakeCurrent(display, 0, 0);
+ // glXMakeCurrent isn't supposed to fail when unsetting the context, unless
+ // we have pending draws on an invalid window - which shouldn't be the case
+ // here.
+ DCHECK(result);
+ if (context_) {
+ glXDestroyContext(display, context_);
+ context_ = NULL;
+ }
+
+ if (pbuffer_) {
+ glXDestroyPbuffer(display, pbuffer_);
+ pbuffer_ = NULL;
+ }
+}
+
+bool PbufferGLContext::MakeCurrent() {
+ if (IsCurrent()) {
+ return true;
+ }
+ Display* display = x11_util::GetXDisplay();
+ if (glXMakeCurrent(display, pbuffer_, context_) != True) {
+ glXDestroyContext(display, context_);
+ context_ = NULL;
+ DLOG(ERROR) << "Couldn't make context current.";
+ return false;
+ }
+
+ return true;
+}
+
+bool PbufferGLContext::IsCurrent() {
+ return glXGetCurrentDrawable() == pbuffer_ &&
+ glXGetCurrentContext() == context_;
+}
+
+bool PbufferGLContext::IsOffscreen() {
+ return true;
+}
+
+void PbufferGLContext::SwapBuffers() {
+ NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
+}
+
+gfx::Size PbufferGLContext::GetSize() {
+ NOTREACHED() << "Should not be requesting size of this pbuffer.";
+ return gfx::Size(1, 1);
+}
+
+void* PbufferGLContext::GetHandle() {
+ return context_;
+}
+
+GLContext* GLContext::CreateOffscreenGLContext(void* shared_handle) {
+ if (!InitializeOneOff())
+ return NULL;
+
+ if (OSMesaCreateContext) {
+ scoped_ptr<OSMesaGLContext> context(new OSMesaGLContext);
+
+ if (!context->Initialize(shared_handle))
+ return NULL;
+
+ return context.release();
+ } else {
+ scoped_ptr<PbufferGLContext> context(new PbufferGLContext);
+ if (!context->Initialize(shared_handle))
+ return NULL;
+
+ return context.release();
+ }
+}
+
+} // namespace gfx