diff options
Diffstat (limited to 'ui/gl/gl_surface_glx.cc')
-rw-r--r-- | ui/gl/gl_surface_glx.cc | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/ui/gl/gl_surface_glx.cc b/ui/gl/gl_surface_glx.cc new file mode 100644 index 0000000..ba8ce92 --- /dev/null +++ b/ui/gl/gl_surface_glx.cc @@ -0,0 +1,326 @@ +// 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/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/process_util.h" +#include "third_party/mesa/MesaLib/include/GL/osmesa.h" +#include "ui/base/x/x11_util.h" +#include "ui/gl/gl_bindings.h" +#include "ui/gl/gl_implementation.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); + } +}; + +Display* g_display; +const char* g_glx_extensions = NULL; +bool g_glx_create_context_robustness_supported = false; + +} // namespace anonymous + +GLSurfaceGLX::GLSurfaceGLX() { +} + +GLSurfaceGLX::~GLSurfaceGLX() { +} + +bool GLSurfaceGLX::InitializeOneOff() { + static bool initialized = false; + if (initialized) + return true; + + g_display = base::MessagePumpForUI::GetDefaultXDisplay(); + 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_create_context_robustness_supported = + HasGLXExtension("GLX_ARB_create_context_robustness"); + + initialized = true; + return true; +} + +// static +const char* GLSurfaceGLX::GetGLXExtensions() { + return g_glx_extensions; +} + +// static +bool GLSurfaceGLX::HasGLXExtension(const char* name) { + DCHECK(name); + const char* c_extensions = GetGLXExtensions(); + if (!c_extensions) + return false; + std::string extensions(c_extensions); + extensions += " "; + + std::string delimited_name(name); + delimited_name += " "; + + return extensions.find(delimited_name) != std::string::npos; +} + +// static +bool GLSurfaceGLX::IsCreateContextRobustnessSupported() { + return g_glx_create_context_robustness_supported; +} + +void* GLSurfaceGLX::GetDisplay() { + return g_display; +} + +NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window) + : window_(window), + config_(NULL) { +} + +NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX() + : window_(0), + config_(NULL) { +} + +NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() { + Destroy(); +} + +bool NativeViewGLSurfaceGLX::Initialize() { + XWindowAttributes attributes; + if (!XGetWindowAttributes(g_display, window_, &attributes)) { + LOG(ERROR) << "XGetWindowAttributes failed for window " << window_ << "."; + return false; + } + size_ = gfx::Size(attributes.width, attributes.height); + return true; +} + +void NativeViewGLSurfaceGLX::Destroy() { +} + +bool NativeViewGLSurfaceGLX::Resize(const gfx::Size& size) { + // On Intel drivers, the frame buffer won't be resize until the next swap. If + // we only do PostSubBuffer, then we're stuck in the old size. Force a swap + // now. + if (gfx::g_GLX_MESA_copy_sub_buffer && size_ != size) + SwapBuffers(); + size_ = size; + return true; +} + +bool NativeViewGLSurfaceGLX::IsOffscreen() { + return false; +} + +bool NativeViewGLSurfaceGLX::SwapBuffers() { + glXSwapBuffers(g_display, window_); + return true; +} + +gfx::Size NativeViewGLSurfaceGLX::GetSize() { + return size_; +} + +void* NativeViewGLSurfaceGLX::GetHandle() { + return reinterpret_cast<void*>(window_); +} + +std::string NativeViewGLSurfaceGLX::GetExtensions() { + std::string extensions = GLSurface::GetExtensions(); + if (g_GLX_MESA_copy_sub_buffer) { + extensions += extensions.empty() ? "" : " "; + extensions += "GL_CHROMIUM_post_sub_buffer"; + } + return extensions; +} + +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, + reinterpret_cast<GLXDrawable>(GetHandle()), + &attributes)) { + LOG(ERROR) << "XGetWindowAttributes failed for window " << + reinterpret_cast<GLXDrawable>(GetHandle()) << "."; + return NULL; + } + + int visual_id = XVisualIDFromVisual(attributes.visual); + + int num_elements = 0; + scoped_ptr_malloc<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(g_GLX_MESA_copy_sub_buffer); + glXCopySubBufferMESA(g_display, window_, x, y, width, height); + return true; +} + +PbufferGLSurfaceGLX::PbufferGLSurfaceGLX(const gfx::Size& size) + : size_(size), + config_(NULL), + pbuffer_(0) { +} + +PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() { + Destroy(); +} + +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_malloc<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_; +} + +} // namespace gfx |