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