diff options
author | apatrick@chromium.org <apatrick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-22 18:32:06 +0000 |
---|---|---|
committer | apatrick@chromium.org <apatrick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-22 18:32:06 +0000 |
commit | 5a6db6cd70a969bc78d931ba352d222db0682ece (patch) | |
tree | a97c62adccbda5956cf9f3511da5d1328c847cd6 /app | |
parent | 2f28c5684b55211bd4ea28b4407128e0ae86ddee (diff) | |
download | chromium_src-5a6db6cd70a969bc78d931ba352d222db0682ece.zip chromium_src-5a6db6cd70a969bc78d931ba352d222db0682ece.tar.gz chromium_src-5a6db6cd70a969bc78d931ba352d222db0682ece.tar.bz2 |
Landing 45240 again.
GLContext implementations were dependent on some stuff in app/.
Moved GLContext class to app/gfx/gl. Now it can be used by code outside of the gpu project, for example AcceleratedSurface.
TEST=trybots, checkdeps
BUG=none
Review URL: http://codereview.chromium.org/1689006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@45335 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'app')
-rw-r--r-- | app/app_base.gypi | 17 | ||||
-rw-r--r-- | app/gfx/gl/gl_context.cc | 86 | ||||
-rw-r--r-- | app/gfx/gl/gl_context.h | 64 | ||||
-rw-r--r-- | app/gfx/gl/gl_context_linux.cc | 402 | ||||
-rw-r--r-- | app/gfx/gl/gl_context_mac.cc | 183 | ||||
-rw-r--r-- | app/gfx/gl/gl_context_osmesa.cc | 109 | ||||
-rw-r--r-- | app/gfx/gl/gl_context_osmesa.h | 57 | ||||
-rw-r--r-- | app/gfx/gl/gl_context_win.cc | 625 |
8 files changed, 1543 insertions, 0 deletions
diff --git a/app/app_base.gypi b/app/app_base.gypi index 802a0d2..f046e2c 100644 --- a/app/app_base.gypi +++ b/app/app_base.gypi @@ -85,6 +85,12 @@ '../third_party/sqlite/sqlite.gyp:sqlite', '../third_party/zlib/zlib.gyp:zlib', ], + 'defines': [ + 'GLEW_STATIC', + ], + 'include_dirs': [ + '../third_party/glew/include', + ], # TODO(gregoryd): The direct_dependent_settings should be shared with # the 64-bit target, but it doesn't work due to a bug in gyp 'direct_dependent_settings': { @@ -117,6 +123,16 @@ 'file_download_interface.h', 'gfx/font_util.h', 'gfx/font_util.cc', + 'gfx/gl/gl_context.cc', + 'gfx/gl/gl_context.h', + 'gfx/gl/gl_context_osmesa.cc', + 'gfx/gl/gl_context_osmesa.h', + 'gfx/gl/gl_context_linux.cc', + 'gfx/gl/gl_context_linux.h', + 'gfx/gl/gl_context_mac.cc', + 'gfx/gl/gl_context_mac.h', + 'gfx/gl/gl_context_win.cc', + 'gfx/gl/gl_context_win.h', 'gtk_dnd_util.cc', 'gtk_dnd_util.h', 'gtk_signal.cc', @@ -181,6 +197,7 @@ 'x11_util.cc', 'x11_util.h', 'x11_util_internal.h', + '../third_party/glew/src/glew.c', ], 'conditions': [ ['OS=="linux" or OS=="freebsd" or OS=="openbsd"', { diff --git a/app/gfx/gl/gl_context.cc b/app/gfx/gl/gl_context.cc new file mode 100644 index 0000000..f4e7073 --- /dev/null +++ b/app/gfx/gl/gl_context.cc @@ -0,0 +1,86 @@ +// Copyright (c) 2009 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. + +#include <GL/glew.h> + +#include "app/gfx/gl/gl_context.h" +#include "base/logging.h" + +namespace gfx { + +// 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. +bool InitializeGLEW() { +#if defined(UNIT_TEST) + return true; +#else + static bool initialized = false; + if (initialized) + return true; + + // Initializes context-dependent parts of GLEW. + if (glewInit() != GLEW_OK) { + LOG(ERROR) << "GLEW failed initialization"; + return false; + } + + // Check to see that we can use the OpenGL vertex attribute APIs + // TODO(petersont): Return false if this check fails, but because some + // Intel hardware does not support OpenGL 2.0, yet does support all of the + // extensions we require, we only log an error. A future CL should change + // this check to ensure that all of the extension strings we require are + // present. + if (!GLEW_VERSION_2_0) { + DLOG(ERROR) << "GL drivers do not have OpenGL 2.0 functionality."; + } + + // Check for necessary extensions. + bool extensions_found = true; + if (!GLEW_ARB_vertex_buffer_object) { + // NOTE: Linux NVidia drivers claim to support OpenGL 2.0 when using + // indirect rendering (e.g. remote X), but it is actually lying. The + // ARB_vertex_buffer_object functions silently no-op (!) when using + // indirect rendering, leading to crashes. Fortunately, in that case, the + // driver claims to not support ARB_vertex_buffer_object, so fail in that + // case. + DLOG(ERROR) << "GL drivers do not support vertex buffer objects."; + extensions_found = false; + } + if (!GLEW_EXT_framebuffer_object) { + DLOG(ERROR) << "GL drivers do not support framebuffer objects."; + extensions_found = false; + } + if (!GLEW_VERSION_2_0 && !GLEW_EXT_stencil_two_side) { + DLOG(ERROR) << "Two sided stencil extension missing."; + extensions_found = false; + } + if (!GLEW_VERSION_1_4 && !GLEW_EXT_blend_func_separate) { + DLOG(ERROR) <<"Separate blend func extension missing."; + extensions_found = false; + } + if (!GLEW_VERSION_2_0 && !GLEW_EXT_blend_equation_separate) { + DLOG(ERROR) << "Separate blend function extension missing."; + extensions_found = false; + } + if (!extensions_found) + return false; + + initialized = true; + return true; +#endif +} + +bool GLContext::InitializeCommon() { + if (!MakeCurrent()) + return false; + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + if (glGetError() != GL_NO_ERROR) + return false; + + return true; +} +} // namespace gfx diff --git a/app/gfx/gl/gl_context.h b/app/gfx/gl/gl_context.h new file mode 100644 index 0000000..2a9b3df --- /dev/null +++ b/app/gfx/gl/gl_context.h @@ -0,0 +1,64 @@ +// 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. + +#ifndef APP_GFX_GL_GL_CONTEXT_H_ +#define APP_GFX_GL_GL_CONTEXT_H_ + +#include "base/logging.h" +#include "build/build_config.h" +#include "gfx/native_widget_types.h" +#include "gfx/size.h" + +namespace gfx { + +bool InitializeGLEW(); + +// Encapsulates an OpenGL context, hiding platform specific management. +class GLContext { + public: + GLContext() {} + virtual ~GLContext() {} + + // Destroys the GL context. + virtual void Destroy() = 0; + + // Makes the GL context current on the current thread. + virtual bool MakeCurrent() = 0; + + // Returns true if this context is current. + virtual bool IsCurrent() = 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; + + // Get the size of the back buffer. + virtual gfx::Size GetSize() = 0; + + // Get the underlying platform specific GL context "handle". + virtual void* GetHandle() = 0; + +#if !defined(OS_MACOSX) + // Create a GL context that renders directly to a view. + static GLContext* CreateViewGLContext(gfx::PluginWindowHandle window, + bool multisampled); +#endif + + // Create 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. + static GLContext* CreateOffscreenGLContext(void* shared_handle); + + protected: + bool InitializeCommon(); + + private: + DISALLOW_COPY_AND_ASSIGN(GLContext); +}; + +} // namespace gfx + +#endif // APP_GFX_GL_GL_CONTEXT_H_ 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 diff --git a/app/gfx/gl/gl_context_mac.cc b/app/gfx/gl/gl_context_mac.cc new file mode 100644 index 0000000..ab08c60 --- /dev/null +++ b/app/gfx/gl/gl_context_mac.cc @@ -0,0 +1,183 @@ +// 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 <GL/glew.h> +#include <GL/osmew.h> +#include <OpenGL/OpenGL.h> + +#include "app/surface/accelerated_surface_mac.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 CGLContextObj GLContextHandle; +typedef CGLPBufferObj PbufferHandle; + +// 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: + 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); +}; + +static bool InitializeOneOff() { + static bool initialized = false; + if (initialized) + return true; + + osmewInit(); + initialized = true; + return true; +} + +bool PbufferGLContext::Initialize(void* shared_handle) { + // Create a 1x1 pbuffer and associated context to bootstrap things. + static const CGLPixelFormatAttribute attribs[] = { + (CGLPixelFormatAttribute) kCGLPFAPBuffer, + (CGLPixelFormatAttribute) 0 + }; + CGLPixelFormatObj pixel_format; + GLint num_pixel_formats; + if (CGLChoosePixelFormat(attribs, + &pixel_format, + &num_pixel_formats) != kCGLNoError) { + DLOG(ERROR) << "Error choosing pixel format."; + Destroy(); + return false; + } + if (!pixel_format) { + return false; + } + CGLError res = CGLCreateContext(pixel_format, + static_cast<GLContextHandle>(shared_handle), + &context_); + CGLDestroyPixelFormat(pixel_format); + if (res != kCGLNoError) { + DLOG(ERROR) << "Error creating context."; + Destroy(); + return false; + } + if (CGLCreatePBuffer(1, 1, + GL_TEXTURE_2D, GL_RGBA, + 0, &pbuffer_) != kCGLNoError) { + DLOG(ERROR) << "Error creating pbuffer."; + Destroy(); + return false; + } + if (CGLSetPBuffer(context_, pbuffer_, 0, 0, 0) != kCGLNoError) { + DLOG(ERROR) << "Error attaching pbuffer to context."; + Destroy(); + 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() { + if (context_) { + CGLDestroyContext(context_); + context_ = NULL; + } + + if (pbuffer_) { + CGLDestroyPBuffer(pbuffer_); + pbuffer_ = NULL; + } +} + +bool PbufferGLContext::MakeCurrent() { + if (!IsCurrent()) { + if (CGLSetCurrentContext(context_) != kCGLNoError) { + DLOG(ERROR) << "Unable to make gl context current."; + return false; + } + } + + return true; +} + +bool PbufferGLContext::IsCurrent() { + return CGLGetCurrentContext() == context_; +} + +bool PbufferGLContext::IsOffscreen() { + return true; +} + +void PbufferGLContext::SwapBuffers() { + NOTREACHED() << "Cannot call SwapBuffers on a PbufferGLContext."; +} + +gfx::Size PbufferGLContext::GetSize() { + NOTREACHED() << "Should not be requesting size of a PbufferGLContext."; + 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 diff --git a/app/gfx/gl/gl_context_osmesa.cc b/app/gfx/gl/gl_context_osmesa.cc new file mode 100644 index 0000000..86f795e --- /dev/null +++ b/app/gfx/gl/gl_context_osmesa.cc @@ -0,0 +1,109 @@ +// Copyright (c) 2009 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. + +#include <GL/glew.h> +#include <GL/osmew.h> + +#include <algorithm> + +#include "app/gfx/gl/gl_context_osmesa.h" + +namespace gfx { + +OSMesaGLContext::OSMesaGLContext() +#if !defined(UNIT_TEST) + : context_(NULL) +#endif +{ +} + +OSMesaGLContext::~OSMesaGLContext() { +} + +bool OSMesaGLContext::Initialize(void* shared_handle) { +#if !defined(UNIT_TEST) + DCHECK(!context_); + + size_ = gfx::Size(1, 1); + buffer_.reset(new int32[1]); + + context_ = OSMesaCreateContext(GL_RGBA, + static_cast<OSMesaContext>(shared_handle)); + return context_ != NULL; +#else + return true; +#endif +} + +void OSMesaGLContext::Destroy() { +#if !defined(UNIT_TEST) + if (context_) { + OSMesaDestroyContext(static_cast<OSMesaContext>(context_)); + context_ = NULL; + } +#endif +} + +bool OSMesaGLContext::MakeCurrent() { +#if !defined(UNIT_TEST) + DCHECK(context_); + return OSMesaMakeCurrent(static_cast<OSMesaContext>(context_), + buffer_.get(), + GL_UNSIGNED_BYTE, + size_.width(), size_.height()) == GL_TRUE; +#endif + return true; +} + +bool OSMesaGLContext::IsCurrent() { +#if !defined(UNIT_TEST) + DCHECK(context_); + return context_ == OSMesaGetCurrentContext(); +#endif + return true; +} + +bool OSMesaGLContext::IsOffscreen() { + return true; +} + +void OSMesaGLContext::SwapBuffers() { + NOTREACHED() << "Should not call SwapBuffers on an OSMesaGLContext."; +} + +gfx::Size OSMesaGLContext::GetSize() { + return size_; +} + +void* OSMesaGLContext::GetHandle() { + return context_; +} + +void OSMesaGLContext::Resize(const gfx::Size& new_size) { + if (new_size == size_) + return; + + // Allocate a new back buffer. + scoped_array<int32> new_buffer(new int32[new_size.GetArea()]); + memset(new_buffer.get(), 0, new_size.GetArea() * sizeof(new_buffer[0])); + + // Copy the current back buffer into the new buffer. + int copy_width = std::min(size_.width(), new_size.width()); + int copy_height = std::min(size_.height(), new_size.height()); + for (int y = 0; y < copy_height; ++y) { + for (int x = 0; x < copy_width; ++x) { + new_buffer[y * new_size.width() + x] = buffer_[y * size_.width() + x]; + } + } + + buffer_.reset(new_buffer.release()); + size_ = new_size; + + // If this context is current, need to call MakeCurrent again so OSMesa uses + // the new buffer. + if (IsCurrent()) + MakeCurrent(); +} + +} // namespace gfx diff --git a/app/gfx/gl/gl_context_osmesa.h b/app/gfx/gl/gl_context_osmesa.h new file mode 100644 index 0000000..52738de --- /dev/null +++ b/app/gfx/gl/gl_context_osmesa.h @@ -0,0 +1,57 @@ +// 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. + +#ifndef APP_GFX_GL_GL_CONTEXT_OSMESA_H_ +#define APP_GFX_GL_GL_CONTEXT_OSMESA_H_ + +#include "base/scoped_ptr.h" +#include "gfx/size.h" +#include "app/gfx/gl/gl_context.h" + +typedef struct osmesa_context *OSMesaContext; + +namespace gfx { + +// Encapsulates an OSMesa OpenGL context that uses software rendering. +class OSMesaGLContext : public GLContext { + public: + OSMesaGLContext(); + virtual ~OSMesaGLContext(); + + // Initialize an OSMesa GL context with the default 1 x 1 initial size. + bool Initialize(void* shared_handle); + + // Implement GLContext. + virtual void Destroy(); + virtual bool MakeCurrent(); + virtual bool IsCurrent(); + virtual bool IsOffscreen(); + virtual void SwapBuffers(); + virtual gfx::Size GetSize(); + virtual void* GetHandle(); + + // Resize the back buffer, preserving the old content. Does nothing if the + // size is unchanged. + void Resize(const gfx::Size& new_size); + + const void* buffer() const { + return buffer_.get(); + } + + protected: + bool InitializeCommon(); + + private: +#if !defined(UNIT_TEST) + gfx::Size size_; + scoped_array<int32> buffer_; + OSMesaContext context_; +#endif + + DISALLOW_COPY_AND_ASSIGN(OSMesaGLContext); +}; + +} // namespace gfx + +#endif // APP_GFX_GL_GL_CONTEXT_OSMESA_H_ diff --git a/app/gfx/gl/gl_context_win.cc b/app/gfx/gl/gl_context_win.cc new file mode 100644 index 0000000..b3a8243 --- /dev/null +++ b/app/gfx/gl/gl_context_win.cc @@ -0,0 +1,625 @@ +// 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 NativeViewGLContext and PbufferGLContext classes. + +#include <GL/glew.h> +#include <GL/osmew.h> +#include <GL/wglew.h> +#include <windows.h> + +#include <algorithm> + +#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 HGLRC GLContextHandle; +typedef HPBUFFERARB PbufferHandle; + +// This class is a wrapper around a GL context that renders directly to a +// window. +class NativeViewGLContext : public GLContext { + public: + explicit NativeViewGLContext(gfx::PluginWindowHandle window) + : window_(window), + device_context_(NULL), + 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_; + HDC device_context_; + GLContextHandle context_; + + DISALLOW_COPY_AND_ASSIGN(NativeViewGLContext); +}; + +// This class is a wrapper around a GL context that uses OSMesa to render +// to an offscreen buffer and then blits it to a window. +class OSMesaViewGLContext : public GLContext { + public: + explicit OSMesaViewGLContext(gfx::PluginWindowHandle window) + : window_(window), + device_context_(NULL) { + DCHECK(window); + } + + // Initializes the GL context. + bool Initialize(); + + virtual void Destroy(); + virtual bool MakeCurrent(); + virtual bool IsCurrent(); + virtual bool IsOffscreen(); + virtual void SwapBuffers(); + virtual gfx::Size GetSize(); + virtual void* GetHandle(); + + private: + void UpdateSize(); + + gfx::PluginWindowHandle window_; + HDC device_context_; + OSMesaGLContext osmesa_context_; + + DISALLOW_COPY_AND_ASSIGN(OSMesaViewGLContext); +}; + +// 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: + PbufferGLContext() + : context_(NULL), + device_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_; + HDC device_context_; + PbufferHandle pbuffer_; + + DISALLOW_COPY_AND_ASSIGN(PbufferGLContext); +}; + +static int g_regular_pixel_format = 0; +static int g_multisampled_pixel_format = 0; + +const PIXELFORMATDESCRIPTOR kPixelFormatDescriptor = { + sizeof(kPixelFormatDescriptor), // Size of structure. + 1, // Default version. + PFD_DRAW_TO_WINDOW | // Window drawing support. + PFD_SUPPORT_OPENGL | // OpenGL support. + PFD_DOUBLEBUFFER, // Double buffering support (not stereo). + PFD_TYPE_RGBA, // RGBA color mode (not indexed). + 24, // 24 bit color mode. + 0, 0, 0, 0, 0, 0, // Don't set RGB bits & shifts. + 8, 0, // 8 bit alpha + 0, // No accumulation buffer. + 0, 0, 0, 0, // Ignore accumulation bits. + 24, // 24 bit z-buffer size. + 8, // 8-bit stencil buffer. + 0, // No aux buffer. + PFD_MAIN_PLANE, // Main drawing plane (not overlay). + 0, // Reserved. + 0, 0, 0, // Layer masks ignored. +}; + +LRESULT CALLBACK IntermediateWindowProc(HWND window, + UINT message, + WPARAM w_param, + LPARAM l_param) { + return ::DefWindowProc(window, message, w_param, l_param); +} + +// Helper routine that does one-off initialization like determining the +// pixel format and initializing glew. +static bool InitializeOneOff() { + static bool initialized = false; + if (initialized) + return true; + + osmewInit(); + if (!OSMesaCreateContext) { + // We must initialize a GL context before we can determine the multi- + // sampling supported on the current hardware, so we create an intermediate + // window and context here. + HINSTANCE module_handle; + if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + reinterpret_cast<wchar_t*>(IntermediateWindowProc), + &module_handle)) { + return false; + } + + WNDCLASS intermediate_class; + intermediate_class.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + intermediate_class.lpfnWndProc = IntermediateWindowProc; + intermediate_class.cbClsExtra = 0; + intermediate_class.cbWndExtra = 0; + intermediate_class.hInstance = module_handle; + intermediate_class.hIcon = LoadIcon(NULL, IDI_APPLICATION); + intermediate_class.hCursor = LoadCursor(NULL, IDC_ARROW); + intermediate_class.hbrBackground = NULL; + intermediate_class.lpszMenuName = NULL; + intermediate_class.lpszClassName = L"Intermediate GL Window"; + + ATOM class_registration = ::RegisterClass(&intermediate_class); + if (!class_registration) { + return false; + } + + HWND intermediate_window = ::CreateWindow( + reinterpret_cast<wchar_t*>(class_registration), + L"", + WS_OVERLAPPEDWINDOW, + 0, 0, + CW_USEDEFAULT, CW_USEDEFAULT, + NULL, + NULL, + NULL, + NULL); + + if (!intermediate_window) { + ::UnregisterClass(reinterpret_cast<wchar_t*>(class_registration), + module_handle); + return false; + } + + HDC intermediate_dc = ::GetDC(intermediate_window); + g_regular_pixel_format = ::ChoosePixelFormat(intermediate_dc, + &kPixelFormatDescriptor); + if (g_regular_pixel_format == 0) { + DLOG(ERROR) << "Unable to get the pixel format for GL context."; + ::ReleaseDC(intermediate_window, intermediate_dc); + ::DestroyWindow(intermediate_window); + ::UnregisterClass(reinterpret_cast<wchar_t*>(class_registration), + module_handle); + return false; + } + if (!::SetPixelFormat(intermediate_dc, g_regular_pixel_format, + &kPixelFormatDescriptor)) { + DLOG(ERROR) << "Unable to set the pixel format for GL context."; + ::ReleaseDC(intermediate_window, intermediate_dc); + ::DestroyWindow(intermediate_window); + ::UnregisterClass(reinterpret_cast<wchar_t*>(class_registration), + module_handle); + return false; + } + + // Create a temporary GL context to query for multisampled pixel formats. + HGLRC gl_context = ::wglCreateContext(intermediate_dc); + if (::wglMakeCurrent(intermediate_dc, gl_context)) { + // GL context was successfully created and applied to the window's DC. + // Startup GLEW, the GL extensions wrangler. + if (InitializeGLEW()) { + DLOG(INFO) << "Initialized GLEW " << glewGetString(GLEW_VERSION); + } else { + ::wglMakeCurrent(intermediate_dc, NULL); + ::wglDeleteContext(gl_context); + ::ReleaseDC(intermediate_window, intermediate_dc); + ::DestroyWindow(intermediate_window); + ::UnregisterClass(reinterpret_cast<wchar_t*>(class_registration), + module_handle); + return false; + } + + // If the multi-sample extensions are present, query the api to determine + // the pixel format. + if (WGLEW_ARB_pixel_format && WGLEW_ARB_multisample) { + int pixel_attributes[] = { + WGL_SAMPLES_ARB, 4, + WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, + WGL_SUPPORT_OPENGL_ARB, GL_TRUE, + WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, + WGL_COLOR_BITS_ARB, 24, + WGL_ALPHA_BITS_ARB, 8, + WGL_DEPTH_BITS_ARB, 24, + WGL_STENCIL_BITS_ARB, 8, + WGL_DOUBLE_BUFFER_ARB, GL_TRUE, + WGL_SAMPLE_BUFFERS_ARB, GL_TRUE, + 0, 0}; + + float pixel_attributes_f[] = {0, 0}; + unsigned int num_formats; + + // Query for the highest sampling rate supported, starting at 4x. + static const int kSampleCount[] = {4, 2}; + static const int kNumSamples = 2; + for (int sample = 0; sample < kNumSamples; ++sample) { + pixel_attributes[1] = kSampleCount[sample]; + if (GL_TRUE == ::wglChoosePixelFormatARB(intermediate_dc, + pixel_attributes, + pixel_attributes_f, + 1, + &g_multisampled_pixel_format, + &num_formats)) { + break; + } + } + } + } + + ::wglMakeCurrent(intermediate_dc, NULL); + ::wglDeleteContext(gl_context); + ::ReleaseDC(intermediate_window, intermediate_dc); + ::DestroyWindow(intermediate_window); + ::UnregisterClass(reinterpret_cast<wchar_t*>(class_registration), + module_handle); + } + + initialized = true; + + return true; +} + +bool NativeViewGLContext::Initialize(bool multisampled) { + // The GL context will render to this window. + device_context_ = GetDC(window_); + + int pixel_format = + multisampled ? g_multisampled_pixel_format : g_regular_pixel_format; + if (!SetPixelFormat(device_context_, + pixel_format, + &kPixelFormatDescriptor)) { + DLOG(ERROR) << "Unable to set the pixel format for GL context."; + Destroy(); + return false; + } + + context_ = wglCreateContext(device_context_); + if (!context_) { + DLOG(ERROR) << "Failed to create GL context."; + Destroy(); + return false; + } + + if (!MakeCurrent()) { + Destroy(); + return false; + } + + if (!InitializeGLEW()) { + Destroy(); + return false; + } + + if (!InitializeCommon()) { + Destroy(); + return false; + } + + return true; +} + +void NativeViewGLContext::Destroy() { + if (context_) { + wglDeleteContext(context_); + context_ = NULL; + } + + if (window_ && device_context_) + ReleaseDC(window_, device_context_); + + window_ = NULL; + device_context_ = NULL; +} + +bool NativeViewGLContext::MakeCurrent() { + if (IsCurrent()) { + return true; + } + if (!wglMakeCurrent(device_context_, context_)) { + DLOG(ERROR) << "Unable to make gl context current."; + return false; + } + + return true; +} + +bool NativeViewGLContext::IsCurrent() { + return wglGetCurrentDC() == device_context_ && + wglGetCurrentContext() == context_; +} + +bool NativeViewGLContext::IsOffscreen() { + return false; +} + +void NativeViewGLContext::SwapBuffers() { + DCHECK(device_context_); + ::SwapBuffers(device_context_); +} + +gfx::Size NativeViewGLContext::GetSize() { + RECT rect; + CHECK(GetClientRect(window_, &rect)); + return gfx::Size(rect.right - rect.left, rect.bottom - rect.top); +} + +void* NativeViewGLContext::GetHandle() { + return context_; +} + +bool OSMesaViewGLContext::Initialize() { + // The GL context will render to this window. + device_context_ = GetDC(window_); + + if (!osmesa_context_.Initialize(NULL)) { + Destroy(); + return false; + } + + if (!MakeCurrent()) { + Destroy(); + return false; + } + + if (!InitializeGLEW()) { + Destroy(); + return false; + } + + if (!InitializeCommon()) { + Destroy(); + return false; + } + + UpdateSize(); + + return true; +} + +void OSMesaViewGLContext::Destroy() { + osmesa_context_.Destroy(); + + if (window_ && device_context_) + ReleaseDC(window_, device_context_); + + window_ = NULL; + device_context_ = NULL; +} + +bool OSMesaViewGLContext::MakeCurrent() { + return osmesa_context_.MakeCurrent(); +} + +bool OSMesaViewGLContext::IsCurrent() { + return osmesa_context_.IsCurrent(); +} + +bool OSMesaViewGLContext::IsOffscreen() { + return false; +} + +void OSMesaViewGLContext::SwapBuffers() { + DCHECK(device_context_); + + // Update the size before blitting so that the blit size is exactly the same + // as the window. + UpdateSize(); + + gfx::Size size = osmesa_context_.GetSize(); + + BITMAPV4HEADER info = { sizeof(BITMAPV4HEADER) }; + info.bV4Width = size.width(); + info.bV4Height = size.height(); + info.bV4Planes = 1; + info.bV4BitCount = 32; + info.bV4V4Compression = BI_BITFIELDS; + info.bV4RedMask = 0xFF000000; + info.bV4GreenMask = 0x00FF0000; + info.bV4BlueMask = 0x0000FF00; + info.bV4AlphaMask = 0x000000FF; + + // Copy the back buffer to the window's device context. + StretchDIBits(device_context_, + 0, 0, size.width(), size.height(), + 0, 0, size.width(), size.height(), + osmesa_context_.buffer(), + reinterpret_cast<BITMAPINFO*>(&info), + DIB_RGB_COLORS, + SRCCOPY); +} + +gfx::Size OSMesaViewGLContext::GetSize() { + return osmesa_context_.GetSize(); +} + +void* OSMesaViewGLContext::GetHandle() { + return osmesa_context_.GetHandle(); +} + +void OSMesaViewGLContext::UpdateSize() { + // Change back buffer size to that of window. + RECT rect; + GetWindowRect(window_, &rect); + gfx::Size window_size = gfx::Size( + std::max(1, static_cast<int>(rect.right - rect.left)), + std::max(1, static_cast<int>(rect.bottom - rect.top))); + osmesa_context_.Resize(window_size); +} + +GLContext* GLContext::CreateViewGLContext(gfx::PluginWindowHandle window, + bool multisampled) { + if (!InitializeOneOff()) + return NULL; + + if (OSMesaCreateContext) { + scoped_ptr<OSMesaViewGLContext> context(new OSMesaViewGLContext(window)); + + if (!context->Initialize()) + return NULL; + + return context.release(); + } else { + scoped_ptr<NativeViewGLContext> context(new NativeViewGLContext(window)); + + if (!context->Initialize(multisampled)) + return NULL; + + return context.release(); + } +} + +bool PbufferGLContext::Initialize(void* shared_handle) { + // Create a device context compatible with the primary display. + HDC display_device_context = ::CreateDC(L"DISPLAY", NULL, NULL, NULL); + + // Create a 1 x 1 pbuffer suitable for use with the device. This is just + // a stepping stone towards creating a frame buffer object. It doesn't + // matter what size it is. + const int kNoAttributes[] = { 0 }; + pbuffer_ = ::wglCreatePbufferARB(display_device_context, + g_regular_pixel_format, + 1, 1, + kNoAttributes); + ::DeleteDC(display_device_context); + if (!pbuffer_) { + DLOG(ERROR) << "Unable to create pbuffer."; + Destroy(); + return false; + } + + device_context_ = ::wglGetPbufferDCARB(pbuffer_); + if (!device_context_) { + DLOG(ERROR) << "Unable to get pbuffer device context."; + Destroy(); + return false; + } + + context_ = ::wglCreateContext(device_context_); + if (!context_) { + DLOG(ERROR) << "Failed to create GL context."; + Destroy(); + return false; + } + + if (shared_handle) { + if (!wglShareLists(static_cast<GLContextHandle>(shared_handle), context_)) { + DLOG(ERROR) << "Could not share GL contexts."; + Destroy(); + return false; + } + } + + if (!MakeCurrent()) { + Destroy(); + return false; + } + + if (!InitializeGLEW()) { + Destroy(); + return false; + } + + if (!InitializeCommon()) { + Destroy(); + return false; + } + + return true; +} + +void PbufferGLContext::Destroy() { + if (context_) { + wglDeleteContext(context_); + context_ = NULL; + } + + if (pbuffer_ && device_context_) + wglReleasePbufferDCARB(pbuffer_, device_context_); + + device_context_ = NULL; + + if (pbuffer_) { + wglDestroyPbufferARB(pbuffer_); + pbuffer_ = NULL; + } +} + +bool PbufferGLContext::MakeCurrent() { + if (IsCurrent()) { + return true; + } + if (!wglMakeCurrent(device_context_, context_)) { + DLOG(ERROR) << "Unable to make gl context current."; + return false; + } + + return true; +} + +bool PbufferGLContext::IsCurrent() { + return wglGetCurrentDC() == device_context_ && + wglGetCurrentContext() == 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 |