Home | History | Annotate | Download | only in gl
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // This include must be here so that the includes provided transitively
      6 // by gl_surface_egl.h don't make it impossible to compile this code.
      7 #include "third_party/mesa/src/include/GL/osmesa.h"
      8 
      9 #include "ui/gl/gl_surface_egl.h"
     10 
     11 #if defined(OS_ANDROID)
     12 #include <android/native_window_jni.h>
     13 #endif
     14 
     15 #include "base/debug/trace_event.h"
     16 #include "base/logging.h"
     17 #include "base/memory/scoped_ptr.h"
     18 #include "base/message_loop/message_loop.h"
     19 #include "build/build_config.h"
     20 #include "ui/gfx/geometry/rect.h"
     21 #include "ui/gl/egl_util.h"
     22 #include "ui/gl/gl_context.h"
     23 #include "ui/gl/gl_implementation.h"
     24 #include "ui/gl/gl_surface_osmesa.h"
     25 #include "ui/gl/gl_surface_stub.h"
     26 #include "ui/gl/gl_switches.h"
     27 #include "ui/gl/scoped_make_current.h"
     28 #include "ui/gl/sync_control_vsync_provider.h"
     29 
     30 #if defined(USE_X11)
     31 extern "C" {
     32 #include <X11/Xlib.h>
     33 }
     34 #endif
     35 
     36 #if defined (USE_OZONE)
     37 #include "ui/ozone/public/surface_factory_ozone.h"
     38 #endif
     39 
     40 #if !defined(EGL_FIXED_SIZE_ANGLE)
     41 #define EGL_FIXED_SIZE_ANGLE 0x3201
     42 #endif
     43 
     44 using ui::GetLastEGLErrorString;
     45 
     46 namespace gfx {
     47 
     48 namespace {
     49 
     50 EGLConfig g_config;
     51 EGLDisplay g_display;
     52 EGLNativeDisplayType g_native_display;
     53 
     54 const char* g_egl_extensions = NULL;
     55 bool g_egl_create_context_robustness_supported = false;
     56 bool g_egl_sync_control_supported = false;
     57 bool g_egl_window_fixed_size_supported = false;
     58 bool g_egl_surfaceless_context_supported = false;
     59 
     60 class EGLSyncControlVSyncProvider
     61     : public gfx::SyncControlVSyncProvider {
     62  public:
     63   explicit EGLSyncControlVSyncProvider(EGLSurface surface)
     64       : SyncControlVSyncProvider(),
     65         surface_(surface) {
     66   }
     67 
     68   virtual ~EGLSyncControlVSyncProvider() { }
     69 
     70  protected:
     71   virtual bool GetSyncValues(int64* system_time,
     72                              int64* media_stream_counter,
     73                              int64* swap_buffer_counter) OVERRIDE {
     74     uint64 u_system_time, u_media_stream_counter, u_swap_buffer_counter;
     75     bool result = eglGetSyncValuesCHROMIUM(
     76         g_display, surface_, &u_system_time,
     77         &u_media_stream_counter, &u_swap_buffer_counter) == EGL_TRUE;
     78     if (result) {
     79       *system_time = static_cast<int64>(u_system_time);
     80       *media_stream_counter = static_cast<int64>(u_media_stream_counter);
     81       *swap_buffer_counter = static_cast<int64>(u_swap_buffer_counter);
     82     }
     83     return result;
     84   }
     85 
     86   virtual bool GetMscRate(int32* numerator, int32* denominator) OVERRIDE {
     87     return false;
     88   }
     89 
     90  private:
     91   EGLSurface surface_;
     92 
     93   DISALLOW_COPY_AND_ASSIGN(EGLSyncControlVSyncProvider);
     94 };
     95 
     96 }  // namespace
     97 
     98 GLSurfaceEGL::GLSurfaceEGL() {}
     99 
    100 bool GLSurfaceEGL::InitializeOneOff() {
    101   static bool initialized = false;
    102   if (initialized)
    103     return true;
    104 
    105   g_native_display = GetPlatformDefaultEGLNativeDisplay();
    106   g_display = eglGetDisplay(g_native_display);
    107   if (!g_display) {
    108     LOG(ERROR) << "eglGetDisplay failed with error " << GetLastEGLErrorString();
    109     return false;
    110   }
    111 
    112   if (!eglInitialize(g_display, NULL, NULL)) {
    113     LOG(ERROR) << "eglInitialize failed with error " << GetLastEGLErrorString();
    114     return false;
    115   }
    116 
    117   // Choose an EGL configuration.
    118   // On X this is only used for PBuffer surfaces.
    119   static const EGLint kConfigAttribs[] = {
    120     EGL_BUFFER_SIZE, 32,
    121     EGL_ALPHA_SIZE, 8,
    122     EGL_BLUE_SIZE, 8,
    123     EGL_GREEN_SIZE, 8,
    124     EGL_RED_SIZE, 8,
    125     EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
    126     EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
    127     EGL_NONE
    128   };
    129 
    130 #if defined(USE_OZONE)
    131   const EGLint* config_attribs =
    132       ui::SurfaceFactoryOzone::GetInstance()->GetEGLSurfaceProperties(
    133           kConfigAttribs);
    134 #else
    135   const EGLint* config_attribs = kConfigAttribs;
    136 #endif
    137 
    138   EGLint num_configs;
    139   if (!eglChooseConfig(g_display,
    140                        config_attribs,
    141                        NULL,
    142                        0,
    143                        &num_configs)) {
    144     LOG(ERROR) << "eglChooseConfig failed with error "
    145                << GetLastEGLErrorString();
    146     return false;
    147   }
    148 
    149   if (num_configs == 0) {
    150     LOG(ERROR) << "No suitable EGL configs found.";
    151     return false;
    152   }
    153 
    154   if (!eglChooseConfig(g_display,
    155                        config_attribs,
    156                        &g_config,
    157                        1,
    158                        &num_configs)) {
    159     LOG(ERROR) << "eglChooseConfig failed with error "
    160                << GetLastEGLErrorString();
    161     return false;
    162   }
    163 
    164   g_egl_extensions = eglQueryString(g_display, EGL_EXTENSIONS);
    165   g_egl_create_context_robustness_supported =
    166       HasEGLExtension("EGL_EXT_create_context_robustness");
    167   g_egl_sync_control_supported =
    168       HasEGLExtension("EGL_CHROMIUM_sync_control");
    169   g_egl_window_fixed_size_supported =
    170       HasEGLExtension("EGL_ANGLE_window_fixed_size");
    171 
    172   // TODO(oetuaho (at) nvidia.com): Surfaceless is disabled on Android as a temporary
    173   // workaround, since code written for Android WebView takes different paths
    174   // based on whether GL surface objects have underlying EGL surface handles,
    175   // conflicting with the use of surfaceless. See https://crbug.com/382349
    176 #if defined(OS_ANDROID)
    177   DCHECK(!g_egl_surfaceless_context_supported);
    178 #else
    179   // Check if SurfacelessEGL is supported.
    180   g_egl_surfaceless_context_supported =
    181       HasEGLExtension("EGL_KHR_surfaceless_context");
    182   if (g_egl_surfaceless_context_supported) {
    183     // EGL_KHR_surfaceless_context is supported but ensure
    184     // GL_OES_surfaceless_context is also supported. We need a current context
    185     // to query for supported GL extensions.
    186     scoped_refptr<GLSurface> surface = new SurfacelessEGL(Size(1, 1));
    187     scoped_refptr<GLContext> context = GLContext::CreateGLContext(
    188       NULL, surface.get(), PreferIntegratedGpu);
    189     if (!context->MakeCurrent(surface.get()))
    190       g_egl_surfaceless_context_supported = false;
    191 
    192     // Ensure context supports GL_OES_surfaceless_context.
    193     if (g_egl_surfaceless_context_supported) {
    194       g_egl_surfaceless_context_supported = context->HasExtension(
    195           "GL_OES_surfaceless_context");
    196       context->ReleaseCurrent(surface.get());
    197     }
    198   }
    199 #endif
    200 
    201   initialized = true;
    202 
    203   return true;
    204 }
    205 
    206 EGLDisplay GLSurfaceEGL::GetDisplay() {
    207   return g_display;
    208 }
    209 
    210 EGLDisplay GLSurfaceEGL::GetHardwareDisplay() {
    211   return g_display;
    212 }
    213 
    214 EGLNativeDisplayType GLSurfaceEGL::GetNativeDisplay() {
    215   return g_native_display;
    216 }
    217 
    218 const char* GLSurfaceEGL::GetEGLExtensions() {
    219   return g_egl_extensions;
    220 }
    221 
    222 bool GLSurfaceEGL::HasEGLExtension(const char* name) {
    223   return ExtensionsContain(GetEGLExtensions(), name);
    224 }
    225 
    226 bool GLSurfaceEGL::IsCreateContextRobustnessSupported() {
    227   return g_egl_create_context_robustness_supported;
    228 }
    229 
    230 bool GLSurfaceEGL::IsEGLSurfacelessContextSupported() {
    231   return g_egl_surfaceless_context_supported;
    232 }
    233 
    234 GLSurfaceEGL::~GLSurfaceEGL() {}
    235 
    236 NativeViewGLSurfaceEGL::NativeViewGLSurfaceEGL(EGLNativeWindowType window)
    237     : window_(window),
    238       surface_(NULL),
    239       supports_post_sub_buffer_(false),
    240       config_(NULL),
    241       size_(1, 1) {
    242 #if defined(OS_ANDROID)
    243   if (window)
    244     ANativeWindow_acquire(window);
    245 #endif
    246 
    247 #if defined(OS_WIN)
    248   RECT windowRect;
    249   if (GetClientRect(window_, &windowRect))
    250     size_ = gfx::Rect(windowRect).size();
    251 #endif
    252 }
    253 
    254 bool NativeViewGLSurfaceEGL::Initialize() {
    255   return Initialize(scoped_ptr<VSyncProvider>());
    256 }
    257 
    258 bool NativeViewGLSurfaceEGL::Initialize(
    259     scoped_ptr<VSyncProvider> sync_provider) {
    260   DCHECK(!surface_);
    261 
    262   if (!GetDisplay()) {
    263     LOG(ERROR) << "Trying to create surface with invalid display.";
    264     return false;
    265   }
    266 
    267   std::vector<EGLint> egl_window_attributes;
    268 
    269   if (g_egl_window_fixed_size_supported) {
    270     egl_window_attributes.push_back(EGL_FIXED_SIZE_ANGLE);
    271     egl_window_attributes.push_back(EGL_TRUE);
    272     egl_window_attributes.push_back(EGL_WIDTH);
    273     egl_window_attributes.push_back(size_.width());
    274     egl_window_attributes.push_back(EGL_HEIGHT);
    275     egl_window_attributes.push_back(size_.height());
    276   }
    277 
    278   if (gfx::g_driver_egl.ext.b_EGL_NV_post_sub_buffer) {
    279     egl_window_attributes.push_back(EGL_POST_SUB_BUFFER_SUPPORTED_NV);
    280     egl_window_attributes.push_back(EGL_TRUE);
    281   }
    282 
    283   egl_window_attributes.push_back(EGL_NONE);
    284   // Create a surface for the native window.
    285   surface_ = eglCreateWindowSurface(
    286       GetDisplay(), GetConfig(), window_, &egl_window_attributes[0]);
    287 
    288   if (!surface_) {
    289     LOG(ERROR) << "eglCreateWindowSurface failed with error "
    290                << GetLastEGLErrorString();
    291     Destroy();
    292     return false;
    293   }
    294 
    295   EGLint surfaceVal;
    296   EGLBoolean retVal = eglQuerySurface(GetDisplay(),
    297                                       surface_,
    298                                       EGL_POST_SUB_BUFFER_SUPPORTED_NV,
    299                                       &surfaceVal);
    300   supports_post_sub_buffer_ = (surfaceVal && retVal) == EGL_TRUE;
    301 
    302   if (sync_provider)
    303     vsync_provider_.reset(sync_provider.release());
    304   else if (g_egl_sync_control_supported)
    305     vsync_provider_.reset(new EGLSyncControlVSyncProvider(surface_));
    306   return true;
    307 }
    308 
    309 void NativeViewGLSurfaceEGL::Destroy() {
    310   if (surface_) {
    311     if (!eglDestroySurface(GetDisplay(), surface_)) {
    312       LOG(ERROR) << "eglDestroySurface failed with error "
    313                  << GetLastEGLErrorString();
    314     }
    315     surface_ = NULL;
    316   }
    317 }
    318 
    319 EGLConfig NativeViewGLSurfaceEGL::GetConfig() {
    320 #if !defined(USE_X11)
    321   return g_config;
    322 #else
    323   if (!config_) {
    324     // Get a config compatible with the window
    325     DCHECK(window_);
    326     XWindowAttributes win_attribs;
    327     if (!XGetWindowAttributes(GetNativeDisplay(), window_, &win_attribs)) {
    328       return NULL;
    329     }
    330 
    331     // Try matching the window depth with an alpha channel,
    332     // because we're worried the destination alpha width could
    333     // constrain blending precision.
    334     const int kBufferSizeOffset = 1;
    335     const int kAlphaSizeOffset = 3;
    336     EGLint config_attribs[] = {
    337       EGL_BUFFER_SIZE, ~0,
    338       EGL_ALPHA_SIZE, 8,
    339       EGL_BLUE_SIZE, 8,
    340       EGL_GREEN_SIZE, 8,
    341       EGL_RED_SIZE, 8,
    342       EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
    343       EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
    344       EGL_NONE
    345     };
    346     config_attribs[kBufferSizeOffset] = win_attribs.depth;
    347 
    348     EGLint num_configs;
    349     if (!eglChooseConfig(g_display,
    350                          config_attribs,
    351                          &config_,
    352                          1,
    353                          &num_configs)) {
    354       LOG(ERROR) << "eglChooseConfig failed with error "
    355                  << GetLastEGLErrorString();
    356       return NULL;
    357     }
    358 
    359     if (num_configs) {
    360       EGLint config_depth;
    361       if (!eglGetConfigAttrib(g_display,
    362                               config_,
    363                               EGL_BUFFER_SIZE,
    364                               &config_depth)) {
    365         LOG(ERROR) << "eglGetConfigAttrib failed with error "
    366                    << GetLastEGLErrorString();
    367         return NULL;
    368       }
    369 
    370       if (config_depth == win_attribs.depth) {
    371         return config_;
    372       }
    373     }
    374 
    375     // Try without an alpha channel.
    376     config_attribs[kAlphaSizeOffset] = 0;
    377     if (!eglChooseConfig(g_display,
    378                          config_attribs,
    379                          &config_,
    380                          1,
    381                          &num_configs)) {
    382       LOG(ERROR) << "eglChooseConfig failed with error "
    383                  << GetLastEGLErrorString();
    384       return NULL;
    385     }
    386 
    387     if (num_configs == 0) {
    388       LOG(ERROR) << "No suitable EGL configs found.";
    389       return NULL;
    390     }
    391   }
    392   return config_;
    393 #endif
    394 }
    395 
    396 bool NativeViewGLSurfaceEGL::IsOffscreen() {
    397   return false;
    398 }
    399 
    400 bool NativeViewGLSurfaceEGL::SwapBuffers() {
    401   TRACE_EVENT2("gpu", "NativeViewGLSurfaceEGL:RealSwapBuffers",
    402       "width", GetSize().width(),
    403       "height", GetSize().height());
    404 
    405   if (!eglSwapBuffers(GetDisplay(), surface_)) {
    406     DVLOG(1) << "eglSwapBuffers failed with error "
    407              << GetLastEGLErrorString();
    408     return false;
    409   }
    410 
    411   return true;
    412 }
    413 
    414 gfx::Size NativeViewGLSurfaceEGL::GetSize() {
    415   EGLint width;
    416   EGLint height;
    417   if (!eglQuerySurface(GetDisplay(), surface_, EGL_WIDTH, &width) ||
    418       !eglQuerySurface(GetDisplay(), surface_, EGL_HEIGHT, &height)) {
    419     NOTREACHED() << "eglQuerySurface failed with error "
    420                  << GetLastEGLErrorString();
    421     return gfx::Size();
    422   }
    423 
    424   return gfx::Size(width, height);
    425 }
    426 
    427 bool NativeViewGLSurfaceEGL::Resize(const gfx::Size& size) {
    428   if (size == GetSize())
    429     return true;
    430 
    431   size_ = size;
    432 
    433   scoped_ptr<ui::ScopedMakeCurrent> scoped_make_current;
    434   GLContext* current_context = GLContext::GetCurrent();
    435   bool was_current =
    436       current_context && current_context->IsCurrent(this);
    437   if (was_current) {
    438     scoped_make_current.reset(
    439         new ui::ScopedMakeCurrent(current_context, this));
    440     current_context->ReleaseCurrent(this);
    441   }
    442 
    443   Destroy();
    444 
    445   if (!Initialize()) {
    446     LOG(ERROR) << "Failed to resize window.";
    447     return false;
    448   }
    449 
    450   return true;
    451 }
    452 
    453 bool NativeViewGLSurfaceEGL::Recreate() {
    454   Destroy();
    455   if (!Initialize()) {
    456     LOG(ERROR) << "Failed to create surface.";
    457     return false;
    458   }
    459   return true;
    460 }
    461 
    462 EGLSurface NativeViewGLSurfaceEGL::GetHandle() {
    463   return surface_;
    464 }
    465 
    466 bool NativeViewGLSurfaceEGL::SupportsPostSubBuffer() {
    467   return supports_post_sub_buffer_;
    468 }
    469 
    470 bool NativeViewGLSurfaceEGL::PostSubBuffer(
    471     int x, int y, int width, int height) {
    472   DCHECK(supports_post_sub_buffer_);
    473   if (!eglPostSubBufferNV(GetDisplay(), surface_, x, y, width, height)) {
    474     DVLOG(1) << "eglPostSubBufferNV failed with error "
    475              << GetLastEGLErrorString();
    476     return false;
    477   }
    478   return true;
    479 }
    480 
    481 VSyncProvider* NativeViewGLSurfaceEGL::GetVSyncProvider() {
    482   return vsync_provider_.get();
    483 }
    484 
    485 NativeViewGLSurfaceEGL::~NativeViewGLSurfaceEGL() {
    486   Destroy();
    487 #if defined(OS_ANDROID)
    488   if (window_)
    489     ANativeWindow_release(window_);
    490 #endif
    491 }
    492 
    493 void NativeViewGLSurfaceEGL::SetHandle(EGLSurface surface) {
    494   surface_ = surface;
    495 }
    496 
    497 PbufferGLSurfaceEGL::PbufferGLSurfaceEGL(const gfx::Size& size)
    498     : size_(size),
    499       surface_(NULL) {
    500   // Some implementations of Pbuffer do not support having a 0 size. For such
    501   // cases use a (1, 1) surface.
    502   if (size_.GetArea() == 0)
    503     size_.SetSize(1, 1);
    504 }
    505 
    506 bool PbufferGLSurfaceEGL::Initialize() {
    507   EGLSurface old_surface = surface_;
    508 
    509   EGLDisplay display = GetDisplay();
    510   if (!display) {
    511     LOG(ERROR) << "Trying to create surface with invalid display.";
    512     return false;
    513   }
    514 
    515   // Allocate the new pbuffer surface before freeing the old one to ensure
    516   // they have different addresses. If they have the same address then a
    517   // future call to MakeCurrent might early out because it appears the current
    518   // context and surface have not changed.
    519   const EGLint pbuffer_attribs[] = {
    520     EGL_WIDTH, size_.width(),
    521     EGL_HEIGHT, size_.height(),
    522     EGL_NONE
    523   };
    524 
    525   EGLSurface new_surface = eglCreatePbufferSurface(display,
    526                                                    GetConfig(),
    527                                                    pbuffer_attribs);
    528   if (!new_surface) {
    529     LOG(ERROR) << "eglCreatePbufferSurface failed with error "
    530                << GetLastEGLErrorString();
    531     return false;
    532   }
    533 
    534   if (old_surface)
    535     eglDestroySurface(display, old_surface);
    536 
    537   surface_ = new_surface;
    538   return true;
    539 }
    540 
    541 void PbufferGLSurfaceEGL::Destroy() {
    542   if (surface_) {
    543     if (!eglDestroySurface(GetDisplay(), surface_)) {
    544       LOG(ERROR) << "eglDestroySurface failed with error "
    545                  << GetLastEGLErrorString();
    546     }
    547     surface_ = NULL;
    548   }
    549 }
    550 
    551 EGLConfig PbufferGLSurfaceEGL::GetConfig() {
    552   return g_config;
    553 }
    554 
    555 bool PbufferGLSurfaceEGL::IsOffscreen() {
    556   return true;
    557 }
    558 
    559 bool PbufferGLSurfaceEGL::SwapBuffers() {
    560   NOTREACHED() << "Attempted to call SwapBuffers on a PbufferGLSurfaceEGL.";
    561   return false;
    562 }
    563 
    564 gfx::Size PbufferGLSurfaceEGL::GetSize() {
    565   return size_;
    566 }
    567 
    568 bool PbufferGLSurfaceEGL::Resize(const gfx::Size& size) {
    569   if (size == size_)
    570     return true;
    571 
    572   scoped_ptr<ui::ScopedMakeCurrent> scoped_make_current;
    573   GLContext* current_context = GLContext::GetCurrent();
    574   bool was_current =
    575       current_context && current_context->IsCurrent(this);
    576   if (was_current) {
    577     scoped_make_current.reset(
    578         new ui::ScopedMakeCurrent(current_context, this));
    579   }
    580 
    581   size_ = size;
    582 
    583   if (!Initialize()) {
    584     LOG(ERROR) << "Failed to resize pbuffer.";
    585     return false;
    586   }
    587 
    588   return true;
    589 }
    590 
    591 EGLSurface PbufferGLSurfaceEGL::GetHandle() {
    592   return surface_;
    593 }
    594 
    595 void* PbufferGLSurfaceEGL::GetShareHandle() {
    596 #if defined(OS_ANDROID)
    597   NOTREACHED();
    598   return NULL;
    599 #else
    600   if (!gfx::g_driver_egl.ext.b_EGL_ANGLE_query_surface_pointer)
    601     return NULL;
    602 
    603   if (!gfx::g_driver_egl.ext.b_EGL_ANGLE_surface_d3d_texture_2d_share_handle)
    604     return NULL;
    605 
    606   void* handle;
    607   if (!eglQuerySurfacePointerANGLE(g_display,
    608                                    GetHandle(),
    609                                    EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE,
    610                                    &handle)) {
    611     return NULL;
    612   }
    613 
    614   return handle;
    615 #endif
    616 }
    617 
    618 PbufferGLSurfaceEGL::~PbufferGLSurfaceEGL() {
    619   Destroy();
    620 }
    621 
    622 SurfacelessEGL::SurfacelessEGL(const gfx::Size& size)
    623     : size_(size) {
    624 }
    625 
    626 bool SurfacelessEGL::Initialize() {
    627   return true;
    628 }
    629 
    630 void SurfacelessEGL::Destroy() {
    631 }
    632 
    633 EGLConfig SurfacelessEGL::GetConfig() {
    634   return g_config;
    635 }
    636 
    637 bool SurfacelessEGL::IsOffscreen() {
    638   return true;
    639 }
    640 
    641 bool SurfacelessEGL::SwapBuffers() {
    642   LOG(ERROR) << "Attempted to call SwapBuffers with SurfacelessEGL.";
    643   return false;
    644 }
    645 
    646 gfx::Size SurfacelessEGL::GetSize() {
    647   return size_;
    648 }
    649 
    650 bool SurfacelessEGL::Resize(const gfx::Size& size) {
    651   size_ = size;
    652   return true;
    653 }
    654 
    655 EGLSurface SurfacelessEGL::GetHandle() {
    656   return EGL_NO_SURFACE;
    657 }
    658 
    659 void* SurfacelessEGL::GetShareHandle() {
    660   return NULL;
    661 }
    662 
    663 SurfacelessEGL::~SurfacelessEGL() {
    664 }
    665 
    666 }  // namespace gfx
    667