// 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. #include #include "build/build_config.h" #if defined(OS_LINUX) #include "app/x11_util.h" #define EGL_HAS_PBUFFERS 1 #endif #include "base/logging.h" #include "base/scoped_ptr.h" #include "app/gfx/gl/gl_bindings.h" #include "app/gfx/gl/gl_context_egl.h" namespace gfx { namespace { // The EGL configuration to use. EGLDisplay g_display; EGLConfig g_config; // Returns the last EGL error as a string. const char* GetLastEGLErrorString() { EGLint error = eglGetError(); switch (error) { case EGL_SUCCESS: return "EGL_SUCCESS"; case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS"; case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC"; case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE"; case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT"; case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG"; case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE"; case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY"; case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE"; case EGL_BAD_MATCH: return "EGL_BAD_MATCH"; case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER"; case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP"; case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW"; default: return "UNKNOWN"; } } } // namespace anonymous bool BaseEGLContext::InitializeOneOff() { static bool initialized = false; if (initialized) return true; #ifdef OS_LINUX EGLNativeDisplayType native_display = x11_util::GetXDisplay(); #else EGLNativeDisplayType native_display = EGL_DEFAULT_DISPLAY; #endif g_display = eglGetDisplay(native_display); if (!g_display) { LOG(ERROR) << "eglGetDisplay failed with error " << GetLastEGLErrorString(); return false; } if (!eglInitialize(g_display, NULL, NULL)) { LOG(ERROR) << "eglInitialize failed with error " << GetLastEGLErrorString(); return false; } // Choose an EGL configuration. static const EGLint kConfigAttribs[] = { EGL_BUFFER_SIZE, 32, EGL_ALPHA_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_RED_SIZE, 8, EGL_DEPTH_SIZE, 16, EGL_STENCIL_SIZE, 8, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, #ifdef EGL_HAS_PBUFFERS EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT, #else EGL_SURFACE_TYPE, EGL_WINDOW_BIT, #endif EGL_NONE }; EGLint num_configs; if (!eglChooseConfig(g_display, kConfigAttribs, NULL, 0, &num_configs)) { LOG(ERROR) << "eglChooseConfig failed failed with error " << GetLastEGLErrorString(); return false; } if (num_configs == 0) { LOG(ERROR) << "No suitable EGL configs found."; return false; } scoped_array configs(new EGLConfig[num_configs]); if (!eglChooseConfig(g_display, kConfigAttribs, configs.get(), num_configs, &num_configs)) { LOG(ERROR) << "eglChooseConfig failed with error " << GetLastEGLErrorString(); return false; } g_config = configs[0]; initialized = true; return true; } EGLDisplay BaseEGLContext::GetDisplay() { return g_display; } std::string BaseEGLContext::GetExtensions() { const char* extensions = eglQueryString(g_display, EGL_EXTENSIONS); if (!extensions) return GLContext::GetExtensions(); return GLContext::GetExtensions() + " " + extensions; } NativeViewEGLContext::NativeViewEGLContext(void* window) : window_(window), surface_(NULL), context_(NULL) { } NativeViewEGLContext::~NativeViewEGLContext() { } bool NativeViewEGLContext::Initialize() { DCHECK(!context_); // Create a surface for the native window. EGLNativeWindowType native_window = reinterpret_cast(window_); surface_ = eglCreateWindowSurface(g_display, g_config, native_window, NULL); if (!surface_) { LOG(ERROR) << "eglCreateWindowSurface failed with error " << GetLastEGLErrorString(); Destroy(); return false; } static const EGLint kContextAttributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; context_ = eglCreateContext(g_display, g_config, NULL, kContextAttributes); if (!context_) { LOG(ERROR) << "eglCreateContext failed with error " << GetLastEGLErrorString(); Destroy(); return false; } if (!MakeCurrent()) { LOG(ERROR) << "MakeCurrent failed."; Destroy(); return false; } if (!InitializeCommon()) { LOG(ERROR) << "GLContext::InitializeCommon failed."; Destroy(); return false; } return true; } void NativeViewEGLContext::Destroy() { if (context_) { if (!eglDestroyContext(g_display, context_)) { LOG(ERROR) << "eglDestroyContext failed with error " << GetLastEGLErrorString(); } context_ = NULL; } if (surface_) { if (!eglDestroySurface(g_display, surface_)) { LOG(ERROR) << "eglDestroySurface failed with error " << GetLastEGLErrorString(); } surface_ = NULL; } } bool NativeViewEGLContext::MakeCurrent() { DCHECK(context_); if (!eglMakeCurrent(g_display, surface_, surface_, context_)) { VLOG(1) << "eglMakeCurrent failed with error " << GetLastEGLErrorString(); return false; } return true; } bool NativeViewEGLContext::IsCurrent() { DCHECK(context_); return context_ == eglGetCurrentContext(); } bool NativeViewEGLContext::IsOffscreen() { return false; } bool NativeViewEGLContext::SwapBuffers() { if (!eglSwapBuffers(g_display, surface_)) { VLOG(1) << "eglSwapBuffers failed with error " << GetLastEGLErrorString(); return false; } return true; } gfx::Size NativeViewEGLContext::GetSize() { #if defined(OS_WIN) RECT rect; if (!GetClientRect(static_cast(window_), &rect)) { DCHECK(false) << "GetClientRect failed."; return gfx::Size(); } return gfx::Size(rect.right - rect.left, rect.bottom - rect.top); #else // TODO(piman): This doesn't work correctly on Windows yet, the size doesn't // get updated on resize. When it does, we can share the code. EGLint width; EGLint height; if (!eglQuerySurface(g_display, surface_, EGL_WIDTH, &width) || !eglQuerySurface(g_display, surface_, EGL_HEIGHT, &height)) { NOTREACHED() << "eglQuerySurface failed with error " << GetLastEGLErrorString(); return gfx::Size(); } return gfx::Size(width, height); #endif } void* NativeViewEGLContext::GetHandle() { return context_; } void NativeViewEGLContext::SetSwapInterval(int interval) { DCHECK(IsCurrent()); if (!eglSwapInterval(g_display, interval)) { LOG(ERROR) << "eglSwapInterval failed with error " << GetLastEGLErrorString(); } } EGLSurface NativeViewEGLContext::GetSurface() { return surface_; } SecondaryEGLContext::SecondaryEGLContext() : surface_(NULL), own_surface_(false), context_(NULL) { } SecondaryEGLContext::~SecondaryEGLContext() { } bool SecondaryEGLContext::Initialize(GLContext* shared_context) { DCHECK(!context_); static const EGLint kContextAttributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; if (shared_context) { surface_ = static_cast(shared_context)->GetSurface(); own_surface_ = false; // Create a context. context_ = eglCreateContext(g_display, g_config, shared_context->GetHandle(), kContextAttributes); } else { #ifdef EGL_HAS_PBUFFERS static const EGLint kPbufferAttribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; surface_ = eglCreatePbufferSurface(g_display, g_config, kPbufferAttribs); if (!surface_) { LOG(ERROR) << "eglCreatePbufferSurface failed with error " << GetLastEGLErrorString(); return false; } own_surface_ = true; context_ = eglCreateContext(g_display, g_config, NULL, kContextAttributes); #else NOTIMPLEMENTED() << "Offscreen non-shared GLES context"; return false; #endif } if (!context_) { LOG(ERROR) << "eglCreateContext failed with error " << GetLastEGLErrorString(); Destroy(); return false; } return true; } void SecondaryEGLContext::Destroy() { if (own_surface_) { if (!eglDestroySurface(g_display, surface_)) { LOG(ERROR) << "eglDestroySurface failed with error " << GetLastEGLErrorString(); } own_surface_ = false; } surface_ = NULL; if (context_) { if (!eglDestroyContext(g_display, context_)) { LOG(ERROR) << "eglDestroyContext failed with error " << GetLastEGLErrorString(); } context_ = NULL; } } bool SecondaryEGLContext::MakeCurrent() { DCHECK(context_); if (!eglMakeCurrent(g_display, surface_, surface_, context_)) { VLOG(1) << "eglMakeCurrent failed with error " << GetLastEGLErrorString(); return false; } return true; } bool SecondaryEGLContext::IsCurrent() { DCHECK(context_); return context_ == eglGetCurrentContext(); } bool SecondaryEGLContext::IsOffscreen() { return true; } bool SecondaryEGLContext::SwapBuffers() { NOTREACHED() << "Attempted to call SwapBuffers on a SecondaryEGLContext."; return false; } gfx::Size SecondaryEGLContext::GetSize() { NOTREACHED() << "Should not be requesting size of this SecondaryEGLContext."; return gfx::Size(1, 1); } void* SecondaryEGLContext::GetHandle() { return context_; } void SecondaryEGLContext::SetSwapInterval(int interval) { DCHECK(IsCurrent()); NOTREACHED() << "Attempt to call SetSwapInterval on a SecondaryEGLContext."; } EGLSurface SecondaryEGLContext::GetSurface() { return surface_; } } // namespace gfx