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