root/ui/gl/gl_surface_glx.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. window_
  2. GetSyncValues
  3. GetMscRate
  4. Create
  5. vsync_lock_
  6. cancel_vsync_flag
  7. vsync_lock
  8. Initialize
  9. GetVSyncParameters
  10. vsync_lock_
  11. GetVSyncParameters
  12. PendingCallbackRunner
  13. AddParentChildPair
  14. RemoveParentChildPair
  15. WillProcessEvent
  16. DidProcessEvent
  17. InitializeOneOff
  18. GetGLXExtensions
  19. HasGLXExtension
  20. IsCreateContextSupported
  21. IsCreateContextRobustnessSupported
  22. IsTextureFromPixmapSupported
  23. IsOMLSyncControlSupported
  24. GetDisplay
  25. SetBackbufferAllocation
  26. SetFrontbufferAllocation
  27. AdjustBufferAllocation
  28. CreateChildWindow
  29. DestroyChildWindow
  30. config_
  31. GetDrawableHandle
  32. Initialize
  33. Destroy
  34. Resize
  35. IsOffscreen
  36. SwapBuffers
  37. GetSize
  38. GetHandle
  39. SupportsPostSubBuffer
  40. GetConfig
  41. PostSubBuffer
  42. GetVSyncProvider
  43. config_
  44. pbuffer_
  45. Initialize
  46. Destroy
  47. IsOffscreen
  48. SwapBuffers
  49. GetSize
  50. GetHandle
  51. GetConfig

// 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_surface_glx.h"

#include "base/basictypes.h"
#include "base/debug/trace_event.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/synchronization/cancellation_flag.h"
#include "base/synchronization/lock.h"
#include "base/threading/non_thread_safe.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "third_party/mesa/src/include/GL/osmesa.h"
#include "ui/gfx/x/x11_types.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/sync_control_vsync_provider.h"

namespace gfx {

namespace {

// scoped_ptr functor for XFree(). Use as follows:
//   scoped_ptr<XVisualInfo, ScopedPtrXFree> foo(...);
// where "XVisualInfo" is any X type that is freed with XFree.
struct ScopedPtrXFree {
  void operator()(void* x) const {
    ::XFree(x);
  }
};

Display* g_display = NULL;
const char* g_glx_extensions = NULL;
bool g_glx_context_create = false;
bool g_glx_create_context_robustness_supported = false;
bool g_glx_texture_from_pixmap_supported = false;
bool g_glx_oml_sync_control_supported = false;

// Track support of glXGetMscRateOML separately from GLX_OML_sync_control as a
// whole since on some platforms (e.g. crosbug.com/34585), glXGetMscRateOML
// always fails even though GLX_OML_sync_control is reported as being supported.
bool g_glx_get_msc_rate_oml_supported = false;

bool g_glx_sgi_video_sync_supported = false;

class OMLSyncControlVSyncProvider
    : public gfx::SyncControlVSyncProvider {
 public:
  explicit OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window)
      : SyncControlVSyncProvider(),
        window_(window) {
  }

  virtual ~OMLSyncControlVSyncProvider() { }

 protected:
  virtual bool GetSyncValues(int64* system_time,
                             int64* media_stream_counter,
                             int64* swap_buffer_counter) OVERRIDE {
    return glXGetSyncValuesOML(g_display, window_, system_time,
                               media_stream_counter, swap_buffer_counter);
  }

  virtual bool GetMscRate(int32* numerator, int32* denominator) OVERRIDE {
    if (!g_glx_get_msc_rate_oml_supported)
      return false;

    if (!glXGetMscRateOML(g_display, window_, numerator, denominator)) {
      // Once glXGetMscRateOML has been found to fail, don't try again,
      // since each failing call may spew an error message.
      g_glx_get_msc_rate_oml_supported = false;
      return false;
    }

    return true;
  }

 private:
  XID window_;

  DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider);
};

class SGIVideoSyncThread
     : public base::Thread,
       public base::NonThreadSafe,
       public base::RefCounted<SGIVideoSyncThread> {
 public:
  static scoped_refptr<SGIVideoSyncThread> Create() {
    if (!g_video_sync_thread) {
      g_video_sync_thread = new SGIVideoSyncThread();
      g_video_sync_thread->Start();
    }
    return g_video_sync_thread;
  }

 private:
  friend class base::RefCounted<SGIVideoSyncThread>;

  SGIVideoSyncThread() : base::Thread("SGI_video_sync") {
    DCHECK(CalledOnValidThread());
  }

  virtual ~SGIVideoSyncThread() {
    DCHECK(CalledOnValidThread());
    g_video_sync_thread = NULL;
    Stop();
  }

  static SGIVideoSyncThread* g_video_sync_thread;

  DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncThread);
};

class SGIVideoSyncProviderThreadShim {
 public:
  explicit SGIVideoSyncProviderThreadShim(XID window)
      : window_(window),
        context_(NULL),
        message_loop_(base::MessageLoopProxy::current()),
        cancel_vsync_flag_(),
        vsync_lock_() {
    // This ensures that creation of |window_| has occured when this shim
    // is executing in the same process as the call to create |window_|.
    XSync(g_display, False);
  }

  virtual ~SGIVideoSyncProviderThreadShim() {
    if (context_) {
      glXDestroyContext(display_, context_);
      context_ = NULL;
    }
  }

  base::CancellationFlag* cancel_vsync_flag() {
    return &cancel_vsync_flag_;
  }

  base::Lock* vsync_lock() {
    return &vsync_lock_;
  }

  void Initialize() {
    DCHECK(display_);

    XWindowAttributes attributes;
    if (!XGetWindowAttributes(display_, window_, &attributes)) {
      LOG(ERROR) << "XGetWindowAttributes failed for window " <<
        window_ << ".";
      return;
    }

    XVisualInfo visual_info_template;
    visual_info_template.visualid = XVisualIDFromVisual(attributes.visual);

    int visual_info_count = 0;
    scoped_ptr<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;
    }

    context_ = glXCreateContext(display_, visual_info_list.get(), NULL, True);

    DCHECK(NULL != context_);
  }

  void GetVSyncParameters(const VSyncProvider::UpdateVSyncCallback& callback) {
    base::TimeTicks now;
    {
      // Don't allow |window_| destruction while we're probing vsync.
      base::AutoLock locked(vsync_lock_);

      if (!context_ || cancel_vsync_flag_.IsSet())
        return;

      glXMakeCurrent(display_, window_, context_);

      unsigned int retrace_count = 0;
      if (glXWaitVideoSyncSGI(1, 0, &retrace_count) != 0)
        return;

      TRACE_EVENT_INSTANT0("gpu", "vblank", TRACE_EVENT_SCOPE_THREAD);
      now = base::TimeTicks::HighResNow();

      glXMakeCurrent(display_, 0, 0);
    }

    const base::TimeDelta kDefaultInterval =
        base::TimeDelta::FromSeconds(1) / 60;

    message_loop_->PostTask(
        FROM_HERE, base::Bind(callback, now, kDefaultInterval));
  }

 private:
  // For initialization of display_ in GLSurface::InitializeOneOff before
  // the sandbox goes up.
  friend class gfx::GLSurfaceGLX;

  static Display* display_;

  XID window_;
  GLXContext context_;

  scoped_refptr<base::MessageLoopProxy> message_loop_;

  base::CancellationFlag cancel_vsync_flag_;
  base::Lock vsync_lock_;

  DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncProviderThreadShim);
};

class SGIVideoSyncVSyncProvider
    : public gfx::VSyncProvider,
      public base::SupportsWeakPtr<SGIVideoSyncVSyncProvider> {
 public:
  explicit SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget window)
      : vsync_thread_(SGIVideoSyncThread::Create()),
        shim_(new SGIVideoSyncProviderThreadShim(window)),
        cancel_vsync_flag_(shim_->cancel_vsync_flag()),
        vsync_lock_(shim_->vsync_lock()) {
    vsync_thread_->message_loop()->PostTask(
        FROM_HERE,
        base::Bind(&SGIVideoSyncProviderThreadShim::Initialize,
                   base::Unretained(shim_.get())));
  }

  virtual ~SGIVideoSyncVSyncProvider() {
    {
      base::AutoLock locked(*vsync_lock_);
      cancel_vsync_flag_->Set();
    }

    // Hand-off |shim_| to be deleted on the |vsync_thread_|.
    vsync_thread_->message_loop()->DeleteSoon(
        FROM_HERE,
        shim_.release());
  }

  virtual void GetVSyncParameters(
      const VSyncProvider::UpdateVSyncCallback& callback) OVERRIDE {
    // Only one outstanding request per surface.
    if (!pending_callback_) {
      pending_callback_.reset(
          new VSyncProvider::UpdateVSyncCallback(callback));
      vsync_thread_->message_loop()->PostTask(
          FROM_HERE,
          base::Bind(&SGIVideoSyncProviderThreadShim::GetVSyncParameters,
                     base::Unretained(shim_.get()),
                     base::Bind(
                         &SGIVideoSyncVSyncProvider::PendingCallbackRunner,
                         AsWeakPtr())));
    }
  }

 private:
  void PendingCallbackRunner(const base::TimeTicks timebase,
                             const base::TimeDelta interval) {
    DCHECK(pending_callback_);
    pending_callback_->Run(timebase, interval);
    pending_callback_.reset();
  }

  scoped_refptr<SGIVideoSyncThread> vsync_thread_;

  // Thread shim through which the sync provider is accessed on |vsync_thread_|.
  scoped_ptr<SGIVideoSyncProviderThreadShim> shim_;

  scoped_ptr<VSyncProvider::UpdateVSyncCallback> pending_callback_;

  // Raw pointers to sync primitives owned by the shim_.
  // These will only be referenced before we post a task to destroy
  // the shim_, so they are safe to access.
  base::CancellationFlag* cancel_vsync_flag_;
  base::Lock* vsync_lock_;

  DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncVSyncProvider);
};

SGIVideoSyncThread* SGIVideoSyncThread::g_video_sync_thread = NULL;

// In order to take advantage of GLX_SGI_video_sync, we need a display
// for use on a separate thread. We must allocate this before the sandbox
// goes up (rather than on-demand when we start the thread).
Display* SGIVideoSyncProviderThreadShim::display_ = NULL;

#if defined(TOOLKIT_GTK)
// A mechanism for forwarding XExpose events from one window to another.
// Because in the workaround for http://crbug.com/145600 the child window
// is placed on top of the parent window, only the child window will receive
// all expose events. These need to be forwared to the parent window to inform
// it that it should paint.
class XExposeEventForwarder : public base::MessagePumpObserver {
 public:
  XExposeEventForwarder() {}
  virtual ~XExposeEventForwarder() {
    DCHECK(child_to_parent_map_.empty());
  }
  void AddParentChildPair(gfx::AcceleratedWidget parent_window,
                          gfx::AcceleratedWidget child_window) {
    if (child_to_parent_map_.empty())
      base::MessagePumpX11::Current()->AddObserver(this);

    DCHECK(child_to_parent_map_.find(child_window) ==
           child_to_parent_map_.end());
    child_to_parent_map_.insert(std::make_pair(
        child_window, parent_window));
  }
  void RemoveParentChildPair(gfx::AcceleratedWidget parent_window,
                             gfx::AcceleratedWidget child_window) {
    DCHECK(child_to_parent_map_.find(child_window) !=
           child_to_parent_map_.end());
    child_to_parent_map_.erase(child_window);

    if (child_to_parent_map_.empty())
      base::MessagePumpX11::Current()->RemoveObserver(this);
  }

 private:
  virtual base::EventStatus WillProcessEvent (
      const base::NativeEvent& xevent) OVERRIDE {
    if (xevent->type != Expose)
      return base::EVENT_CONTINUE;

    WindowMap::const_iterator found = child_to_parent_map_.find(
        xevent->xexpose.window);
    if (found == child_to_parent_map_.end())
      return base::EVENT_CONTINUE;

    gfx::AcceleratedWidget target_window = found->second;
    XEvent forwarded_event = *xevent;
    forwarded_event.xexpose.window = target_window;
    XSendEvent(g_display, target_window, False, ExposureMask,
               &forwarded_event);
    return base::EVENT_CONTINUE;
  }
  virtual void DidProcessEvent(const base::NativeEvent& xevent) OVERRIDE {
  }

  typedef std::map<gfx::AcceleratedWidget, gfx::AcceleratedWidget> WindowMap;
  WindowMap child_to_parent_map_;

  DISALLOW_COPY_AND_ASSIGN(XExposeEventForwarder);
};

static base::LazyInstance<XExposeEventForwarder> g_xexpose_event_forwarder =
    LAZY_INSTANCE_INITIALIZER;

// Do not use this workaround when running in test harnesses that do not have
// a message loop or do not have a TYPE_GPU message loop.
bool g_create_child_windows = false;
#endif

}  // namespace

GLSurfaceGLX::GLSurfaceGLX() {}

bool GLSurfaceGLX::InitializeOneOff() {
  static bool initialized = false;
  if (initialized)
    return true;

  // http://crbug.com/245466
  setenv("force_s3tc_enable", "true", 1);

  // SGIVideoSyncProviderShim (if instantiated) will issue X commands on
  // it's own thread.
  XInitThreads();

#if defined(TOOLKIT_GTK)
  // Be sure to use the X display handle and not the GTK display handle if this
  // is the GPU process.
  g_create_child_windows =
      base::MessageLoop::current() &&
      base::MessageLoop::current()->type() == base::MessageLoop::TYPE_GPU;

  if (g_create_child_windows)
    g_display = base::MessagePumpX11::GetDefaultXDisplay();
  else
    g_display = base::MessagePumpForUI::GetDefaultXDisplay();
#else
  g_display = base::MessagePumpForUI::GetDefaultXDisplay();
#endif

  if (!g_display) {
    LOG(ERROR) << "XOpenDisplay failed.";
    return false;
  }

  int major, minor;
  if (!glXQueryVersion(g_display, &major, &minor)) {
    LOG(ERROR) << "glxQueryVersion failed";
    return false;
  }

  if (major == 1 && minor < 3) {
    LOG(ERROR) << "GLX 1.3 or later is required.";
    return false;
  }

  g_glx_extensions = glXQueryExtensionsString(g_display, 0);
  g_glx_context_create =
      HasGLXExtension("GLX_ARB_create_context");
  g_glx_create_context_robustness_supported =
      HasGLXExtension("GLX_ARB_create_context_robustness");
  g_glx_texture_from_pixmap_supported =
      HasGLXExtension("GLX_EXT_texture_from_pixmap");
  g_glx_oml_sync_control_supported =
      HasGLXExtension("GLX_OML_sync_control");
  g_glx_get_msc_rate_oml_supported = g_glx_oml_sync_control_supported;
  g_glx_sgi_video_sync_supported =
      HasGLXExtension("GLX_SGI_video_sync");

  if (!g_glx_get_msc_rate_oml_supported && g_glx_sgi_video_sync_supported)
    SGIVideoSyncProviderThreadShim::display_ = XOpenDisplay(NULL);

  initialized = true;
  return true;
}

// static
const char* GLSurfaceGLX::GetGLXExtensions() {
  return g_glx_extensions;
}

// static
bool GLSurfaceGLX::HasGLXExtension(const char* name) {
  return ExtensionsContain(GetGLXExtensions(), name);
}

// static
bool GLSurfaceGLX::IsCreateContextSupported() {
  return g_glx_context_create;
}

// static
bool GLSurfaceGLX::IsCreateContextRobustnessSupported() {
  return g_glx_create_context_robustness_supported;
}

// static
bool GLSurfaceGLX::IsTextureFromPixmapSupported() {
  return g_glx_texture_from_pixmap_supported;
}

// static
bool GLSurfaceGLX::IsOMLSyncControlSupported() {
  return g_glx_oml_sync_control_supported;
}

void* GLSurfaceGLX::GetDisplay() {
  return g_display;
}

GLSurfaceGLX::~GLSurfaceGLX() {}

#if defined(TOOLKIT_GTK)
bool NativeViewGLSurfaceGLX::SetBackbufferAllocation(bool allocated) {
  backbuffer_allocated_ = allocated;
  AdjustBufferAllocation();
  return true;
}

void NativeViewGLSurfaceGLX::SetFrontbufferAllocation(bool allocated) {
  frontbuffer_allocated_ = allocated;
  AdjustBufferAllocation();
}

void NativeViewGLSurfaceGLX::AdjustBufferAllocation() {
  if (!g_create_child_windows)
    return;

  if (frontbuffer_allocated_ || backbuffer_allocated_)
    CreateChildWindow();
  else
    DestroyChildWindow();
}

void NativeViewGLSurfaceGLX::CreateChildWindow() {
  DCHECK(g_create_child_windows);

  if (child_window_)
    return;

  XSetWindowAttributes set_window_attributes;
  set_window_attributes.event_mask = ExposureMask;
  child_window_ = XCreateWindow(
      g_display, parent_window_, 0, 0, size_.width(), size_.height(), 0,
      CopyFromParent, InputOutput, CopyFromParent, CWEventMask,
      &set_window_attributes);
  g_xexpose_event_forwarder.Pointer()->AddParentChildPair(
      parent_window_, child_window_);

  XMapWindow(g_display, child_window_);
  XFlush(g_display);
}

void NativeViewGLSurfaceGLX::DestroyChildWindow() {
  if (!child_window_)
    return;

  g_xexpose_event_forwarder.Pointer()->RemoveParentChildPair(
      parent_window_, child_window_);
  XDestroyWindow(g_display, child_window_);
  XFlush(g_display);
  child_window_ = 0;
}
#endif

NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window)
  : parent_window_(window),
#if defined(TOOLKIT_GTK)
    child_window_(0),
    dummy_window_(0),
    backbuffer_allocated_(true),
    frontbuffer_allocated_(true),
#endif
    config_(NULL) {
}

gfx::AcceleratedWidget NativeViewGLSurfaceGLX::GetDrawableHandle() const {
#if defined(TOOLKIT_GTK)
  if (g_create_child_windows) {
    if (child_window_)
      return child_window_;
    return dummy_window_;
  }
#endif
  return parent_window_;
}

bool NativeViewGLSurfaceGLX::Initialize() {
  XWindowAttributes attributes;
  if (!XGetWindowAttributes(g_display, parent_window_, &attributes)) {
    LOG(ERROR) << "XGetWindowAttributes failed for window " << parent_window_
        << ".";
    return false;
  }
  size_ = gfx::Size(attributes.width, attributes.height);

  gfx::AcceleratedWidget window_for_vsync = parent_window_;

#if defined(TOOLKIT_GTK)
  if (g_create_child_windows) {
    dummy_window_ = XCreateWindow(
        g_display,
        RootWindow(g_display, XScreenNumberOfScreen(attributes.screen)),
        0, 0, 1, 1, 0, CopyFromParent, InputOutput, attributes.visual, 0, NULL);
    window_for_vsync = dummy_window_;
    CreateChildWindow();
  }
#endif

  if (g_glx_oml_sync_control_supported)
    vsync_provider_.reset(new OMLSyncControlVSyncProvider(window_for_vsync));
  else if (g_glx_sgi_video_sync_supported)
    vsync_provider_.reset(new SGIVideoSyncVSyncProvider(window_for_vsync));

  return true;
}

void NativeViewGLSurfaceGLX::Destroy() {
#if defined(TOOLKIT_GTK)
  DestroyChildWindow();
  if (dummy_window_)
    XDestroyWindow(g_display, dummy_window_);
  dummy_window_ = 0;
#endif
}

bool NativeViewGLSurfaceGLX::Resize(const gfx::Size& size) {
#if defined(TOOLKIT_GTK)
  if (child_window_) {
    XResizeWindow(g_display, child_window_, size.width(), size.height());
    XFlush(g_display);
  }
#endif
  size_ = size;
  return true;
}

bool NativeViewGLSurfaceGLX::IsOffscreen() {
  return false;
}

bool NativeViewGLSurfaceGLX::SwapBuffers() {
  TRACE_EVENT2("gpu", "NativeViewGLSurfaceGLX:RealSwapBuffers",
      "width", GetSize().width(),
      "height", GetSize().height());

  glXSwapBuffers(g_display, GetDrawableHandle());
  return true;
}

gfx::Size NativeViewGLSurfaceGLX::GetSize() {
  return size_;
}

void* NativeViewGLSurfaceGLX::GetHandle() {
  return reinterpret_cast<void*>(GetDrawableHandle());
}

bool NativeViewGLSurfaceGLX::SupportsPostSubBuffer() {
  return gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer;
}

void* NativeViewGLSurfaceGLX::GetConfig() {
  if (!config_) {
    // This code path is expensive, but we only take it when
    // attempting to use GLX_ARB_create_context_robustness, in which
    // case we need a GLXFBConfig for the window in order to create a
    // context for it.
    //
    // TODO(kbr): this is not a reliable code path. On platforms which
    // support it, we should use glXChooseFBConfig in the browser
    // process to choose the FBConfig and from there the X Visual to
    // use when creating the window in the first place. Then we can
    // pass that FBConfig down rather than attempting to reconstitute
    // it.

    XWindowAttributes attributes;
    if (!XGetWindowAttributes(
        g_display,
        parent_window_,
        &attributes)) {
      LOG(ERROR) << "XGetWindowAttributes failed for window " <<
          parent_window_ << ".";
      return NULL;
    }

    int visual_id = XVisualIDFromVisual(attributes.visual);

    int num_elements = 0;
    scoped_ptr<GLXFBConfig, ScopedPtrXFree> configs(
        glXGetFBConfigs(g_display,
                        DefaultScreen(g_display),
                        &num_elements));
    if (!configs.get()) {
      LOG(ERROR) << "glXGetFBConfigs failed.";
      return NULL;
    }
    if (!num_elements) {
      LOG(ERROR) << "glXGetFBConfigs returned 0 elements.";
      return NULL;
    }
    bool found = false;
    int i;
    for (i = 0; i < num_elements; ++i) {
      int value;
      if (glXGetFBConfigAttrib(
              g_display, configs.get()[i], GLX_VISUAL_ID, &value)) {
        LOG(ERROR) << "glXGetFBConfigAttrib failed.";
        return NULL;
      }
      if (value == visual_id) {
        found = true;
        break;
      }
    }
    if (found) {
      config_ = configs.get()[i];
    }
  }

  return config_;
}

bool NativeViewGLSurfaceGLX::PostSubBuffer(
    int x, int y, int width, int height) {
  DCHECK(gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer);
  glXCopySubBufferMESA(g_display, GetDrawableHandle(), x, y, width, height);
  return true;
}

VSyncProvider* NativeViewGLSurfaceGLX::GetVSyncProvider() {
  return vsync_provider_.get();
}

NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX()
  : parent_window_(0),
#if defined(TOOLKIT_GTK)
    child_window_(0),
    dummy_window_(0),
    backbuffer_allocated_(true),
    frontbuffer_allocated_(true),
#endif
    config_(NULL) {
}

NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
  Destroy();
}

PbufferGLSurfaceGLX::PbufferGLSurfaceGLX(const gfx::Size& size)
  : size_(size),
    config_(NULL),
    pbuffer_(0) {
}

bool PbufferGLSurfaceGLX::Initialize() {
  DCHECK(!pbuffer_);

  static const int config_attributes[] = {
    GLX_BUFFER_SIZE, 32,
    GLX_ALPHA_SIZE, 8,
    GLX_BLUE_SIZE, 8,
    GLX_GREEN_SIZE, 8,
    GLX_RED_SIZE, 8,
    GLX_RENDER_TYPE, GLX_RGBA_BIT,
    GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
    GLX_DOUBLEBUFFER, False,
    0
  };

  int num_elements = 0;
  scoped_ptr<GLXFBConfig, ScopedPtrXFree> configs(
      glXChooseFBConfig(g_display,
                        DefaultScreen(g_display),
                        config_attributes,
                        &num_elements));
  if (!configs.get()) {
    LOG(ERROR) << "glXChooseFBConfig failed.";
    return false;
  }
  if (!num_elements) {
    LOG(ERROR) << "glXChooseFBConfig returned 0 elements.";
    return false;
  }

  config_ = configs.get()[0];

  const int pbuffer_attributes[] = {
    GLX_PBUFFER_WIDTH, size_.width(),
    GLX_PBUFFER_HEIGHT, size_.height(),
    0
  };
  pbuffer_ = glXCreatePbuffer(g_display,
                              static_cast<GLXFBConfig>(config_),
                              pbuffer_attributes);
  if (!pbuffer_) {
    Destroy();
    LOG(ERROR) << "glXCreatePbuffer failed.";
    return false;
  }

  return true;
}

void PbufferGLSurfaceGLX::Destroy() {
  if (pbuffer_) {
    glXDestroyPbuffer(g_display, pbuffer_);
    pbuffer_ = 0;
  }

  config_ = NULL;
}

bool PbufferGLSurfaceGLX::IsOffscreen() {
  return true;
}

bool PbufferGLSurfaceGLX::SwapBuffers() {
  NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
  return false;
}

gfx::Size PbufferGLSurfaceGLX::GetSize() {
  return size_;
}

void* PbufferGLSurfaceGLX::GetHandle() {
  return reinterpret_cast<void*>(pbuffer_);
}

void* PbufferGLSurfaceGLX::GetConfig() {
  return config_;
}

PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() {
  Destroy();
}

}  // namespace gfx

/* [<][>][^][v][top][bottom][index][help] */