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