• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
EGLSyncControlVSyncProvider(EGLSurface surface)63   explicit EGLSyncControlVSyncProvider(EGLSurface surface)
64       : SyncControlVSyncProvider(),
65         surface_(surface) {
66   }
67 
~EGLSyncControlVSyncProvider()68   virtual ~EGLSyncControlVSyncProvider() { }
69 
70  protected:
GetSyncValues(int64 * system_time,int64 * media_stream_counter,int64 * swap_buffer_counter)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 
GetMscRate(int32 * numerator,int32 * denominator)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 
GLSurfaceEGL()98 GLSurfaceEGL::GLSurfaceEGL() {}
99 
InitializeOneOff()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@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 
GetDisplay()206 EGLDisplay GLSurfaceEGL::GetDisplay() {
207   return g_display;
208 }
209 
GetHardwareDisplay()210 EGLDisplay GLSurfaceEGL::GetHardwareDisplay() {
211   return g_display;
212 }
213 
GetNativeDisplay()214 EGLNativeDisplayType GLSurfaceEGL::GetNativeDisplay() {
215   return g_native_display;
216 }
217 
GetEGLExtensions()218 const char* GLSurfaceEGL::GetEGLExtensions() {
219   return g_egl_extensions;
220 }
221 
HasEGLExtension(const char * name)222 bool GLSurfaceEGL::HasEGLExtension(const char* name) {
223   return ExtensionsContain(GetEGLExtensions(), name);
224 }
225 
IsCreateContextRobustnessSupported()226 bool GLSurfaceEGL::IsCreateContextRobustnessSupported() {
227   return g_egl_create_context_robustness_supported;
228 }
229 
IsEGLSurfacelessContextSupported()230 bool GLSurfaceEGL::IsEGLSurfacelessContextSupported() {
231   return g_egl_surfaceless_context_supported;
232 }
233 
~GLSurfaceEGL()234 GLSurfaceEGL::~GLSurfaceEGL() {}
235 
NativeViewGLSurfaceEGL(EGLNativeWindowType window)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 
Initialize()254 bool NativeViewGLSurfaceEGL::Initialize() {
255   return Initialize(scoped_ptr<VSyncProvider>());
256 }
257 
Initialize(scoped_ptr<VSyncProvider> sync_provider)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 
Destroy()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 
GetConfig()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 
IsOffscreen()396 bool NativeViewGLSurfaceEGL::IsOffscreen() {
397   return false;
398 }
399 
SwapBuffers()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 
GetSize()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 
Resize(const gfx::Size & size)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 
Recreate()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 
GetHandle()462 EGLSurface NativeViewGLSurfaceEGL::GetHandle() {
463   return surface_;
464 }
465 
SupportsPostSubBuffer()466 bool NativeViewGLSurfaceEGL::SupportsPostSubBuffer() {
467   return supports_post_sub_buffer_;
468 }
469 
PostSubBuffer(int x,int y,int width,int height)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 
GetVSyncProvider()481 VSyncProvider* NativeViewGLSurfaceEGL::GetVSyncProvider() {
482   return vsync_provider_.get();
483 }
484 
~NativeViewGLSurfaceEGL()485 NativeViewGLSurfaceEGL::~NativeViewGLSurfaceEGL() {
486   Destroy();
487 #if defined(OS_ANDROID)
488   if (window_)
489     ANativeWindow_release(window_);
490 #endif
491 }
492 
SetHandle(EGLSurface surface)493 void NativeViewGLSurfaceEGL::SetHandle(EGLSurface surface) {
494   surface_ = surface;
495 }
496 
PbufferGLSurfaceEGL(const gfx::Size & size)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 
Initialize()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 
Destroy()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 
GetConfig()551 EGLConfig PbufferGLSurfaceEGL::GetConfig() {
552   return g_config;
553 }
554 
IsOffscreen()555 bool PbufferGLSurfaceEGL::IsOffscreen() {
556   return true;
557 }
558 
SwapBuffers()559 bool PbufferGLSurfaceEGL::SwapBuffers() {
560   NOTREACHED() << "Attempted to call SwapBuffers on a PbufferGLSurfaceEGL.";
561   return false;
562 }
563 
GetSize()564 gfx::Size PbufferGLSurfaceEGL::GetSize() {
565   return size_;
566 }
567 
Resize(const gfx::Size & size)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 
GetHandle()591 EGLSurface PbufferGLSurfaceEGL::GetHandle() {
592   return surface_;
593 }
594 
GetShareHandle()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 
~PbufferGLSurfaceEGL()618 PbufferGLSurfaceEGL::~PbufferGLSurfaceEGL() {
619   Destroy();
620 }
621 
SurfacelessEGL(const gfx::Size & size)622 SurfacelessEGL::SurfacelessEGL(const gfx::Size& size)
623     : size_(size) {
624 }
625 
Initialize()626 bool SurfacelessEGL::Initialize() {
627   return true;
628 }
629 
Destroy()630 void SurfacelessEGL::Destroy() {
631 }
632 
GetConfig()633 EGLConfig SurfacelessEGL::GetConfig() {
634   return g_config;
635 }
636 
IsOffscreen()637 bool SurfacelessEGL::IsOffscreen() {
638   return true;
639 }
640 
SwapBuffers()641 bool SurfacelessEGL::SwapBuffers() {
642   LOG(ERROR) << "Attempted to call SwapBuffers with SurfacelessEGL.";
643   return false;
644 }
645 
GetSize()646 gfx::Size SurfacelessEGL::GetSize() {
647   return size_;
648 }
649 
Resize(const gfx::Size & size)650 bool SurfacelessEGL::Resize(const gfx::Size& size) {
651   size_ = size;
652   return true;
653 }
654 
GetHandle()655 EGLSurface SurfacelessEGL::GetHandle() {
656   return EGL_NO_SURFACE;
657 }
658 
GetShareHandle()659 void* SurfacelessEGL::GetShareHandle() {
660   return NULL;
661 }
662 
~SurfacelessEGL()663 SurfacelessEGL::~SurfacelessEGL() {
664 }
665 
666 }  // namespace gfx
667