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