summaryrefslogtreecommitdiffstats
path: root/gpu
diff options
context:
space:
mode:
authorkbr@google.com <kbr@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-31 17:55:57 +0000
committerkbr@google.com <kbr@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-31 17:55:57 +0000
commit28638b2ce5ec96bda25fd3583d7902dea0137fb2 (patch)
tree585a58803f76da08e806524086f0948f7393b663 /gpu
parent74f275448f43dac31331495148e832c376b18344 (diff)
downloadchromium_src-28638b2ce5ec96bda25fd3583d7902dea0137fb2.zip
chromium_src-28638b2ce5ec96bda25fd3583d7902dea0137fb2.tar.gz
chromium_src-28638b2ce5ec96bda25fd3583d7902dea0137fb2.tar.bz2
Implemented offscreen rendering path for GLES2CmdDecoder on Linux.
With these changes, WebGL runs in the sandbox on Linux. BUG=29120 TEST=ran WebGL demos in sandbox on Linux Review URL: http://codereview.chromium.org/1540004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@43235 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gpu')
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.cc27
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.h12
-rw-r--r--gpu/command_buffer/service/gpu_processor_linux.cc29
-rw-r--r--gpu/command_buffer/service/x_utils.cc222
-rw-r--r--gpu/command_buffer/service/x_utils.h91
5 files changed, 298 insertions, 83 deletions
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index fc44614..63e267d 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -31,7 +31,7 @@
#include "gpu/command_buffer/service/texture_manager.h"
#if defined(UNIT_TEST)
#elif defined(OS_LINUX)
-// XWindowWrapper is stubbed out for unit-tests.
+// GLXContextWrapper is stubbed out for unit-tests.
#include "gpu/command_buffer/service/x_utils.h"
#elif defined(OS_MACOSX)
#include "app/surface/accelerated_surface_mac.h"
@@ -260,7 +260,7 @@ GLES2Decoder::GLES2Decoder(ContextGroup* group)
debug_(false) {
#elif defined(OS_LINUX)
debug_(false),
- window_(NULL) {
+ context_(NULL) {
#elif defined(OS_WIN)
debug_(false),
hwnd_(NULL) {
@@ -1543,7 +1543,7 @@ bool GLES2DecoderImpl::MakeCurrent() {
return true;
#elif defined(OS_LINUX)
// TODO(apatrick): offscreen rendering not yet supported on this platform.
- return window()->MakeCurrent();
+ return context()->MakeCurrent();
#elif defined(OS_MACOSX)
if (gl_context_) {
if (CGLGetCurrentContext() != gl_context_) {
@@ -1607,7 +1607,7 @@ void GLES2DecoderImpl::UnregisterObjects(
}
bool GLES2DecoderImpl::InitPlatformSpecific() {
-#if !defined(UNIT_TEST)
+#if !defined(UNIT_TEST) && !defined(OS_LINUX)
bool offscreen = pending_size_.width() > 0 && pending_size_.height() > 0;
#endif
#if defined(UNIT_TEST)
@@ -1672,14 +1672,15 @@ bool GLES2DecoderImpl::InitPlatformSpecific() {
}
#elif defined(OS_LINUX)
- // TODO(apatrick): offscreen rendering not yet supported on this platform.
- DCHECK(!offscreen);
-
// TODO(apatrick): parent contexts not yet supported on this platform.
DCHECK(!parent_);
- DCHECK(window());
- if (!window()->Initialize())
+ DCHECK(context());
+
+ // Offscreen / onscreen handling done earlier on this platform (in
+ // GPUProcessor::Initialize).
+
+ if (!context()->Initialize())
return false;
#elif defined(OS_MACOSX)
// TODO(apatrick): parent contexts not yet supported on this platform.
@@ -1971,8 +1972,8 @@ void GLES2DecoderImpl::Destroy() {
#if defined(UNIT_TEST)
#elif defined(GLES2_GPU_SERVICE_BACKEND_NATIVE_GLES2)
#elif defined(OS_LINUX)
- DCHECK(window());
- window()->Destroy();
+ DCHECK(context());
+ context()->Destroy();
#elif defined(OS_MACOSX)
surface_.Destroy();
#endif
@@ -3677,8 +3678,8 @@ error::Error GLES2DecoderImpl::HandleSwapBuffers(
#elif defined(OS_WIN)
::SwapBuffers(gl_device_context_);
#elif defined(OS_LINUX)
- DCHECK(window());
- window()->SwapBuffers();
+ DCHECK(context());
+ context()->SwapBuffers();
#elif defined(OS_MACOSX)
// TODO(kbr): Need to property hook up and track the OpenGL state and hook
// up the notion of the currently bound FBO.
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.h b/gpu/command_buffer/service/gles2_cmd_decoder.h
index 58e1193..05951a2 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.h
@@ -23,7 +23,7 @@
namespace gpu {
// Forward-declared instead of including x_utils.h, because including glx.h
// causes havok.
-class XWindowWrapper;
+class GLXContextWrapper;
namespace gles2 {
@@ -50,11 +50,11 @@ class GLES2Decoder : public CommonDecoder {
}
#if defined(OS_LINUX)
- void set_window_wrapper(XWindowWrapper *window) {
- window_ = window;
+ void set_context_wrapper(GLXContextWrapper *context) {
+ context_ = context;
}
- XWindowWrapper* window() const {
- return window_;
+ GLXContextWrapper* context() const {
+ return context_;
}
#elif defined(OS_WIN)
void set_hwnd(HWND hwnd) {
@@ -108,7 +108,7 @@ class GLES2Decoder : public CommonDecoder {
bool debug_;
#if defined(OS_LINUX)
- XWindowWrapper *window_;
+ GLXContextWrapper *context_;
#elif defined(OS_WIN)
// Handle to the GL device.
HWND hwnd_;
diff --git a/gpu/command_buffer/service/gpu_processor_linux.cc b/gpu/command_buffer/service/gpu_processor_linux.cc
index d430933..b477021 100644
--- a/gpu/command_buffer/service/gpu_processor_linux.cc
+++ b/gpu/command_buffer/service/gpu_processor_linux.cc
@@ -14,10 +14,8 @@ bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle,
GPUProcessor* parent,
const gfx::Size& size,
uint32 parent_texture_id) {
- DCHECK(handle);
-
// Cannot reinitialize.
- if (decoder_->window() != NULL)
+ if (decoder_->context() != NULL)
return false;
// Map the ring buffer and create the parser.
@@ -34,9 +32,18 @@ bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle,
decoder_.get()));
}
- // Initialize GAPI immediately if the window handle is valid.
- XWindowWrapper *window = new XWindowWrapper(GDK_DISPLAY(), handle);
- decoder_->set_window_wrapper(window);
+ // Initialize GAPI immediately.
+ GLXContextWrapper* wrapper = NULL;
+ if (size.width() > 0 && size.height() > 0) {
+ // Offscreen code path.
+ DCHECK(!handle);
+ wrapper = new GLXPbufferWrapper(GDK_DISPLAY());
+ } else {
+ DCHECK(handle);
+ // Onscreen code path.
+ wrapper = new XWindowWrapper(GDK_DISPLAY(), handle);
+ }
+ decoder_->set_context_wrapper(wrapper);
gles2::GLES2Decoder* parent_decoder = parent ? parent->decoder_.get() : NULL;
if (!decoder_->Initialize(parent_decoder,
size,
@@ -45,15 +52,17 @@ bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle,
return false;
}
- return true;}
+ return true;
+}
void GPUProcessor::Destroy() {
// Destroy decoder if initialized.
+ // TODO(gman): simplify cleanup logic. http://crbug.com/39895
if (decoder_.get()) {
- XWindowWrapper *window = decoder_->window();
+ GLXContextWrapper *context = decoder_->context();
decoder_->Destroy();
- decoder_->set_window_wrapper(NULL);
- delete window;
+ decoder_->set_context_wrapper(NULL);
+ delete context;
decoder_.reset();
}
diff --git a/gpu/command_buffer/service/x_utils.cc b/gpu/command_buffer/service/x_utils.cc
index 7abdf8c..9c10377 100644
--- a/gpu/command_buffer/service/x_utils.cc
+++ b/gpu/command_buffer/service/x_utils.cc
@@ -6,21 +6,33 @@
#include <dlfcn.h>
+#include "base/scoped_ptr.h"
#include "gpu/command_buffer/service/precompile.h"
#include "gpu/command_buffer/common/logging.h"
#include "gpu/command_buffer/service/x_utils.h"
namespace gpu {
+// 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 g_glxew_initialized = false;
static bool g_glew_initialized = false;
-bool XWindowWrapper::Initialize() {
- if (!g_glew_initialized) {
+static bool InitializeGLXEW() {
+ if (!g_glxew_initialized) {
void* handle = dlopen("libGL.so.1", RTLD_LAZY | RTLD_GLOBAL);
if (!handle) {
LOG(ERROR) << "Could not find libGL.so.1";
@@ -29,78 +41,210 @@ bool XWindowWrapper::Initialize() {
// Initializes context-independent parts of GLEW
if (glxewInit() != GLEW_OK) {
- LOG(ERROR) << "GLXEW failed initialization";
+ LOG(ERROR) << "glxewInit failed";
+ return false;
+ }
+ // Hack to work around apparently incorrect assumption in
+ // glxewContextInit. Documentation indicates that all of the work
+ // it does is actually context-independent. We require GLX 1.3
+ // entry points to be present for successful initialization of the
+ // off-screen code path in the GLES2Decoder. TODO(kbr): clean this
+ // up.
+ if (glxewContextInit() != GLEW_OK) {
+ LOG(ERROR) << "glxewContextInit failed";
+ return false;
+ }
+ g_glxew_initialized = true;
+ }
+ return true;
+}
+
+// GLEW initialization is extremely expensive because it looks up
+// hundreds of function pointers. Realistically we are not going to
+// switch between GL implementations on the fly, so for the time being
+// we only do the context-dependent GLEW initialization once.
+static bool InitializeGLEW() {
+ if (!g_glew_initialized) {
+ // Initializes context-dependent parts of GLEW
+ if (glewInit() != GLEW_OK) {
+ LOG(ERROR) << "GLEW failed initialization";
+ return false;
+ }
+
+ if (!glewIsSupported("GL_VERSION_2_0")) {
+ LOG(ERROR) << "GL implementation doesn't support GL version 2.0";
return false;
}
g_glew_initialized = true;
}
+ return true;
+}
+
+GLXContextWrapper::~GLXContextWrapper() {
+}
+
+bool GLXContextWrapper::Initialize() {
+ if (!MakeCurrent()) {
+ Destroy();
+ DLOG(ERROR) << "Couldn't make context current for initialization.";
+ return false;
+ }
+
+ if (!InitializeGLEW()) {
+ Destroy();
+ return false;
+ }
+
+ return true;
+}
+
+void GLXContextWrapper::Destroy() {
+ Bool result = glXMakeCurrent(GetDisplay(), 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 (GetContext()) {
+ glXDestroyContext(GetDisplay(), GetContext());
+ SetContext(NULL);
+ }
+}
+
+bool XWindowWrapper::Initialize() {
+ if (!InitializeGLXEW())
+ return false;
+
XWindowAttributes attributes;
- XGetWindowAttributes(display_, window_, &attributes);
+ XGetWindowAttributes(GetDisplay(), window_, &attributes);
XVisualInfo visual_info_template;
visual_info_template.visualid = XVisualIDFromVisual(attributes.visual);
int visual_info_count = 0;
- XVisualInfo *visual_info_list = XGetVisualInfo(display_, VisualIDMask,
- &visual_info_template,
- &visual_info_count);
- DCHECK(visual_info_list);
+ scoped_ptr_malloc<XVisualInfo, ScopedPtrXFree> visual_info_list(
+ XGetVisualInfo(GetDisplay(), VisualIDMask,
+ &visual_info_template,
+ &visual_info_count));
+ DCHECK(visual_info_list.get());
DCHECK_GT(visual_info_count, 0);
- context_ = 0;
+ SetContext(NULL);
for (int i = 0; i < visual_info_count; ++i) {
- context_ = glXCreateContext(display_, visual_info_list + i, 0,
- True);
- if (context_) break;
+ SetContext(glXCreateContext(GetDisplay(), visual_info_list.get() + i, 0,
+ True));
+ if (GetContext())
+ break;
}
- XFree(visual_info_list);
- if (!context_) {
+ if (!GetContext()) {
DLOG(ERROR) << "Couldn't create GL context.";
return false;
}
- if (!MakeCurrent()) {
+ return GLXContextWrapper::Initialize();
+}
+
+bool XWindowWrapper::MakeCurrent() {
+ if (glXGetCurrentDrawable() == window_ &&
+ glXGetCurrentContext() == GetContext()) {
+ return true;
+ }
+ if (glXMakeCurrent(GetDisplay(), window_, GetContext()) != True) {
+ glXDestroyContext(GetDisplay(), GetContext());
+ SetContext(0);
+ DLOG(ERROR) << "Couldn't make context current.";
return false;
}
+ return true;
+}
+
+bool XWindowWrapper::IsOffscreen() {
+ return false;
+}
+
+void XWindowWrapper::SwapBuffers() {
+ glXSwapBuffers(GetDisplay(), window_);
+}
+
+bool GLXPbufferWrapper::Initialize() {
+ if (!InitializeGLXEW())
+ return false;
- // Initializes context-dependent parts of GLEW
- if (glewInit() != GLEW_OK) {
- LOG(ERROR) << "GLEW failed initialization";
+ if (!glXChooseFBConfig ||
+ !glXCreateNewContext ||
+ !glXCreatePbuffer ||
+ !glXDestroyPbuffer) {
+ DLOG(ERROR) << "Pbuffer support not available.";
return false;
}
- if (!glewIsSupported("GL_VERSION_2_0")) {
- LOG(ERROR) << "GL implementation doesn't support GL version 2.0";
+ static const int config_attributes[] = {
+ GLX_DRAWABLE_TYPE,
+ GLX_PBUFFER_BIT,
+ GLX_RENDER_TYPE,
+ GLX_RGBA_BIT,
+ GLX_DOUBLEBUFFER,
+ 0,
+ 0
+ };
+
+ int nelements = 0;
+ // TODO(kbr): figure out whether hardcoding screen to 0 is sufficient.
+ scoped_ptr_malloc<GLXFBConfig, ScopedPtrXFree> config(
+ glXChooseFBConfig(GetDisplay(), 0, config_attributes, &nelements));
+ if (!config.get()) {
+ DLOG(ERROR) << "glXChooseFBConfig failed.";
+ return false;
+ }
+ if (!nelements) {
+ DLOG(ERROR) << "glXChooseFBConfig returned 0 elements.";
+ return false;
+ }
+ SetContext(glXCreateNewContext(GetDisplay(),
+ config.get()[0], GLX_RGBA_TYPE, 0, True));
+ if (!GetContext()) {
+ DLOG(ERROR) << "glXCreateNewContext failed.";
return false;
}
+ static const int pbuffer_attributes[] = {
+ GLX_PBUFFER_WIDTH,
+ 1,
+ GLX_PBUFFER_HEIGHT,
+ 1,
+ 0
+ };
+ pbuffer_ = glXCreatePbuffer(GetDisplay(),
+ config.get()[0], pbuffer_attributes);
+ if (!pbuffer_) {
+ Destroy();
+ DLOG(ERROR) << "glXCreatePbuffer failed.";
+ return false;
+ }
+ return GLXContextWrapper::Initialize();
+}
- return true;
+void GLXPbufferWrapper::Destroy() {
+ GLXContextWrapper::Destroy();
+ if (pbuffer_) {
+ glXDestroyPbuffer(GetDisplay(), pbuffer_);
+ pbuffer_ = NULL;
+ }
}
-bool XWindowWrapper::MakeCurrent() {
- if (glXGetCurrentDrawable() == window_ &&
- glXGetCurrentContext() == context_) {
+bool GLXPbufferWrapper::MakeCurrent() {
+ if (glXGetCurrentDrawable() == pbuffer_ &&
+ glXGetCurrentContext() == GetContext()) {
return true;
}
- if (glXMakeCurrent(display_, window_, context_) != True) {
- glXDestroyContext(display_, context_);
- context_ = 0;
+ if (glXMakeCurrent(GetDisplay(), pbuffer_, GetContext()) != True) {
+ glXDestroyContext(GetDisplay(), GetContext());
+ SetContext(0);
DLOG(ERROR) << "Couldn't make context current.";
return false;
}
return true;
}
-void XWindowWrapper::Destroy() {
- 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_ = 0;
- }
+bool GLXPbufferWrapper::IsOffscreen() {
+ return true;
}
-void XWindowWrapper::SwapBuffers() {
- glXSwapBuffers(display_, window_);
+void GLXPbufferWrapper::SwapBuffers() {
}
} // namespace gpu
diff --git a/gpu/command_buffer/service/x_utils.h b/gpu/command_buffer/service/x_utils.h
index cd601c6..ca88f73 100644
--- a/gpu/command_buffer/service/x_utils.h
+++ b/gpu/command_buffer/service/x_utils.h
@@ -13,36 +13,97 @@
namespace gpu {
+// Abstracts between on-screen and off-screen GLX contexts.
+class GLXContextWrapper {
+ public:
+ explicit GLXContextWrapper(Display* display)
+ : display_(display),
+ context_(NULL) {
+ DCHECK(display_);
+ }
+
+ virtual ~GLXContextWrapper();
+
+ // Initializes the GL context. Subclasses should perform their
+ // initialization work and then call the superclass implementation.
+ virtual bool Initialize();
+
+ // Destroys the GL context. Subclasses may call this before or after
+ // doing any necessary cleanup work.
+ virtual void Destroy();
+
+ // Makes the GL context current on the current thread.
+ virtual bool MakeCurrent() = 0;
+
+ // Returns true if this context is offscreen.
+ virtual bool IsOffscreen() = 0;
+
+ // Swaps front and back buffers. This has no effect for off-screen
+ // contexts.
+ virtual void SwapBuffers() = 0;
+
+ // Fetches the GLX context for sharing of server-side objects.
+ GLXContext GetContext() {
+ return context_;
+ }
+
+ protected:
+ Display* GetDisplay() {
+ return display_;
+ }
+
+ void SetContext(GLXContext context) {
+ context_ = context;
+ }
+
+ private:
+ Display* display_;
+ GLXContext context_;
+
+ DISALLOW_COPY_AND_ASSIGN(GLXContextWrapper);
+};
+
// This class is a wrapper around an X Window and associated GL context. It is
// useful to isolate intrusive X headers, since it can be forward declared
// (Window and GLXContext can't).
-class XWindowWrapper {
+class XWindowWrapper : public GLXContextWrapper {
public:
XWindowWrapper(Display *display, Window window)
- : display_(display),
+ : GLXContextWrapper(display),
window_(window) {
- DCHECK(display_);
DCHECK(window_);
}
- // Initializes the GL context.
- bool Initialize();
-
- // Destroys the GL context.
- void Destroy();
-
- // Makes the GL context current on the current thread.
- bool MakeCurrent();
- // Swaps front and back buffers.
- void SwapBuffers();
+ virtual bool Initialize();
+ virtual bool MakeCurrent();
+ virtual bool IsOffscreen();
+ virtual void SwapBuffers();
private:
- Display *display_;
Window window_;
- GLXContext context_;
DISALLOW_COPY_AND_ASSIGN(XWindowWrapper);
};
+// This class is a wrapper around a GLX pbuffer and associated GL context. It
+// serves the same purpose as the XWindowWrapper for initializing off-screen
+// rendering.
+class GLXPbufferWrapper : public GLXContextWrapper {
+ public:
+ explicit GLXPbufferWrapper(Display* display)
+ : GLXContextWrapper(display) {
+ }
+
+ virtual bool Initialize();
+ virtual void Destroy();
+ virtual bool MakeCurrent();
+ virtual bool IsOffscreen();
+ virtual void SwapBuffers();
+
+ private:
+ GLXPbuffer pbuffer_;
+ DISALLOW_COPY_AND_ASSIGN(GLXPbufferWrapper);
+};
+
} // namespace gpu
#endif // GPU_COMMAND_BUFFER_SERVICE_X_UTILS_H_